├── .codeclimate.yml ├── .devcontainer ├── Dockerfile ├── README.md ├── chefs_local │ ├── config │ │ ├── freshclam.conf │ │ └── jetstream.conf │ ├── docker-compose.yml │ ├── local.sample.json │ └── test.json ├── devcontainer.json └── post-install.sh ├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── custom.md │ └── feature_request.md ├── actions │ ├── build-push-container │ │ └── action.yaml │ └── deploy-to-environment │ │ └── action.yaml ├── pull_request_template.md └── workflows │ ├── clamav-image-build.yaml │ ├── codeql-analysis.yaml │ ├── cypress-ci.yaml │ ├── on_push.yaml │ ├── pr_deploy.yaml │ ├── pr_undeploy.yaml │ ├── reusable-owasp-zap.yaml │ └── unit-tests.yaml ├── .gitignore ├── .vscode ├── README.md ├── launch.json └── tasks.json ├── CNAME ├── CODE-OF-CONDUCT.md ├── COMPLIANCE.yaml ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── SECURITY.md ├── _config.yml ├── app ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .prettierignore ├── .prettierrc ├── README.md ├── app.js ├── bin │ └── www ├── config │ ├── custom-environment-variables.json │ ├── default.json │ ├── production.json │ └── test.json ├── frontend │ ├── .browserslistrc │ ├── .eslintignore │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── component-update.js │ ├── index.html │ ├── jsconfig.json │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── images │ │ │ ├── drag_drop.png │ │ │ ├── quickstart.png │ │ │ └── team-management.png │ │ └── index.html │ ├── src │ │ ├── App.vue │ │ ├── assets │ │ │ ├── images │ │ │ │ ├── bc_logo.svg │ │ │ │ ├── bc_logo_print.svg │ │ │ │ ├── bc_logo_square.svg │ │ │ │ └── drag-drop-demo.gif │ │ │ └── scss │ │ │ │ └── style.scss │ │ ├── components │ │ │ ├── admin │ │ │ │ ├── AddOwner.vue │ │ │ │ ├── AdminAPIsTable.vue │ │ │ │ ├── AdminFormsTable.vue │ │ │ │ ├── AdminPage.vue │ │ │ │ ├── AdminUsersTable.vue │ │ │ │ ├── AdminVersions.vue │ │ │ │ ├── AdministerForm.vue │ │ │ │ ├── AdministerUser.vue │ │ │ │ ├── Dashboard.vue │ │ │ │ ├── Developer.vue │ │ │ │ └── FormComponentsProactiveHelp.vue │ │ │ ├── base │ │ │ │ ├── BaseAuthButton.vue │ │ │ │ ├── BaseCopyToClipboard.vue │ │ │ │ ├── BaseDialog.vue │ │ │ │ ├── BaseFilter.vue │ │ │ │ ├── BaseImagePopout.vue │ │ │ │ ├── BaseInfoCard.vue │ │ │ │ ├── BaseInternationalization.vue │ │ │ │ ├── BaseNotificationBar.vue │ │ │ │ ├── BaseNotificationContainer.vue │ │ │ │ ├── BasePanel.vue │ │ │ │ ├── BasePrintButton.vue │ │ │ │ └── BaseSecure.vue │ │ │ ├── bcgov │ │ │ │ ├── BCGovAlertBanner.vue │ │ │ │ ├── BCGovFooter.vue │ │ │ │ ├── BCGovHeader.vue │ │ │ │ └── BCGovNavBar.vue │ │ │ ├── designer │ │ │ │ ├── FloatButton.vue │ │ │ │ ├── FormDesigner.vue │ │ │ │ ├── FormDisclaimer.vue │ │ │ │ ├── FormProfile.vue │ │ │ │ ├── FormSettings.vue │ │ │ │ ├── FormViewer.vue │ │ │ │ ├── FormViewerActions.vue │ │ │ │ ├── FormViewerMultiUpload.vue │ │ │ │ ├── FormsTable.vue │ │ │ │ ├── profile │ │ │ │ │ ├── FormAPIProfile.vue │ │ │ │ │ ├── FormDeploymentProfile.vue │ │ │ │ │ ├── FormLabelProfile.vue │ │ │ │ │ ├── FormMinistryProfile.vue │ │ │ │ │ └── FormUseCaseProfile.vue │ │ │ │ └── settings │ │ │ │ │ ├── FormAccessSettings.vue │ │ │ │ │ ├── FormEventStreamSettings.vue │ │ │ │ │ ├── FormFunctionalitySettings.vue │ │ │ │ │ ├── FormGeneralSettings.vue │ │ │ │ │ ├── FormMetadataSettings.vue │ │ │ │ │ ├── FormScheduleSettings.vue │ │ │ │ │ └── FormSubmissionSettings.vue │ │ │ ├── forms │ │ │ │ ├── ExportSubmissions.vue │ │ │ │ ├── FormSubmission.vue │ │ │ │ ├── PrintOptions.vue │ │ │ │ ├── RequestReceipt.vue │ │ │ │ ├── SubmissionsTable.vue │ │ │ │ ├── manage │ │ │ │ │ ├── AddTeamMember.vue │ │ │ │ │ ├── ApiKey.vue │ │ │ │ │ ├── DocumentTemplate.vue │ │ │ │ │ ├── EmailManagement.vue │ │ │ │ │ ├── EmailTemplate.vue │ │ │ │ │ ├── ExternalAPIs.vue │ │ │ │ │ ├── ManageForm.vue │ │ │ │ │ ├── ManageFormActions.vue │ │ │ │ │ ├── ManageLayout.vue │ │ │ │ │ ├── ManageVersions.vue │ │ │ │ │ ├── ShareForm.vue │ │ │ │ │ ├── Subscription.vue │ │ │ │ │ └── TeamManagement.vue │ │ │ │ └── submission │ │ │ │ │ ├── AuditHistory.vue │ │ │ │ │ ├── DeleteSubmission.vue │ │ │ │ │ ├── ManageSubmissionUsers.vue │ │ │ │ │ ├── MySubmissionsActions.vue │ │ │ │ │ ├── MySubmissionsTable.vue │ │ │ │ │ ├── NotesPanel.vue │ │ │ │ │ ├── StatusPanel.vue │ │ │ │ │ ├── StatusTable.vue │ │ │ │ │ ├── UserDuplicateSubmission.vue │ │ │ │ │ └── UserSubmission.vue │ │ │ └── infolinks │ │ │ │ ├── GeneralLayout.vue │ │ │ │ ├── ProactiveHelpDialog.vue │ │ │ │ └── ProactiveHelpPreviewDialog.vue │ │ ├── composables │ │ │ ├── documentTemplate.js │ │ │ ├── form.js │ │ │ └── printOptions.js │ │ ├── favicon.ico │ │ ├── filters │ │ │ └── index.js │ │ ├── internationalization │ │ │ ├── index.js │ │ │ └── trans │ │ │ │ ├── chefs │ │ │ │ ├── ar │ │ │ │ │ ├── ar.json │ │ │ │ │ └── index.js │ │ │ │ ├── de │ │ │ │ │ ├── de.json │ │ │ │ │ └── index.js │ │ │ │ ├── en │ │ │ │ │ ├── en.json │ │ │ │ │ └── index.js │ │ │ │ ├── es │ │ │ │ │ ├── es.json │ │ │ │ │ └── index.js │ │ │ │ ├── fa │ │ │ │ │ ├── fa.json │ │ │ │ │ └── index.js │ │ │ │ ├── fr │ │ │ │ │ ├── fr.json │ │ │ │ │ └── index.js │ │ │ │ ├── hi │ │ │ │ │ ├── hi.json │ │ │ │ │ └── index.js │ │ │ │ ├── index.js │ │ │ │ ├── it │ │ │ │ │ ├── index.js │ │ │ │ │ └── it.json │ │ │ │ ├── ja │ │ │ │ │ ├── index.js │ │ │ │ │ └── ja.json │ │ │ │ ├── ko │ │ │ │ │ ├── index.js │ │ │ │ │ └── ko.json │ │ │ │ ├── pa │ │ │ │ │ ├── index.js │ │ │ │ │ └── pa.json │ │ │ │ ├── pt │ │ │ │ │ ├── index.js │ │ │ │ │ └── pt.json │ │ │ │ ├── ru │ │ │ │ │ ├── index.js │ │ │ │ │ └── ru.json │ │ │ │ ├── tl │ │ │ │ │ ├── index.js │ │ │ │ │ └── tl.json │ │ │ │ ├── uk │ │ │ │ │ ├── index.js │ │ │ │ │ └── uk.json │ │ │ │ ├── vi │ │ │ │ │ ├── index.js │ │ │ │ │ └── vi.json │ │ │ │ ├── zh │ │ │ │ │ ├── index.js │ │ │ │ │ └── zh.json │ │ │ │ └── zhTW │ │ │ │ │ ├── index.js │ │ │ │ │ └── zh-TW.json │ │ │ │ ├── formio │ │ │ │ └── formio.json │ │ │ │ └── vuetify │ │ │ │ └── locale │ │ │ │ ├── README.md │ │ │ │ ├── hi.js │ │ │ │ ├── pa.js │ │ │ │ └── tl.js │ │ ├── main.js │ │ ├── plugins │ │ │ ├── templateExtensions.js │ │ │ └── vuetify.js │ │ ├── router.js │ │ ├── services │ │ │ ├── adminService.js │ │ │ ├── apiKeyService.js │ │ │ ├── encryptionKeyService.js │ │ │ ├── eventStreamConfigService.js │ │ │ ├── fileService.js │ │ │ ├── formService.js │ │ │ ├── index.js │ │ │ ├── interceptors.js │ │ │ ├── rbacService.js │ │ │ ├── roleService.js │ │ │ ├── userService.js │ │ │ └── utilsService.js │ │ ├── store │ │ │ ├── admin.js │ │ │ ├── app.js │ │ │ ├── auth.js │ │ │ ├── form.js │ │ │ ├── identityProviders.js │ │ │ ├── index.js │ │ │ └── notification.js │ │ ├── utils │ │ │ ├── constants.js │ │ │ ├── keycloak.js │ │ │ ├── permissionUtils.js │ │ │ └── transformUtils.js │ │ └── views │ │ │ ├── About.vue │ │ │ ├── Admin.vue │ │ │ ├── Error.vue │ │ │ ├── File.vue │ │ │ ├── Form.vue │ │ │ ├── Login.vue │ │ │ ├── NotFound.vue │ │ │ ├── User.vue │ │ │ ├── admin │ │ │ ├── Form.vue │ │ │ ├── Root.vue │ │ │ └── User.vue │ │ │ ├── file │ │ │ └── Download.vue │ │ │ ├── form │ │ │ ├── Create.vue │ │ │ ├── Design.vue │ │ │ ├── Emails.vue │ │ │ ├── Export.vue │ │ │ ├── Manage.vue │ │ │ ├── Preview.vue │ │ │ ├── PublishForm.vue │ │ │ ├── Submissions.vue │ │ │ ├── Submit.vue │ │ │ ├── Success.vue │ │ │ ├── Teams.vue │ │ │ └── View.vue │ │ │ └── user │ │ │ ├── Forms.vue │ │ │ ├── History.vue │ │ │ ├── Root.vue │ │ │ ├── SubmissionDraftEdit.vue │ │ │ ├── SubmissionDuplicate.vue │ │ │ ├── SubmissionView.vue │ │ │ └── Submissions.vue │ ├── tests │ │ └── unit │ │ │ ├── App.spec.js │ │ │ ├── components │ │ │ ├── admin │ │ │ │ ├── AddOwner.spec.js │ │ │ │ ├── AdminAPIsTable.spec.js │ │ │ │ ├── AdminFormsTable.spec.js │ │ │ │ ├── AdminPage.spec.js │ │ │ │ ├── AdminUsersTable.spec.js │ │ │ │ ├── AdminVersions.spec.js │ │ │ │ ├── AdministerForm.spec.js │ │ │ │ ├── AdministerUser.spec.js │ │ │ │ ├── Dashboard.spec.js │ │ │ │ ├── Developer.spec.js │ │ │ │ └── FormComponentsProactiveHelp.spec.js │ │ │ ├── base │ │ │ │ ├── BaseAuthButton.spec.js │ │ │ │ ├── BaseCopyToClipboard.spec.js │ │ │ │ ├── BaseDialog.spec.js │ │ │ │ ├── BaseFilter.spec.js │ │ │ │ ├── BaseImagePopout.spec.js │ │ │ │ ├── BaseInternationalization.spec.js │ │ │ │ ├── BaseNotificationBar.spec.js │ │ │ │ ├── BaseNotificationContainer.spec.js │ │ │ │ ├── BasePrintButton.spec.js │ │ │ │ └── BaseSecure.spec.js │ │ │ ├── bcgov │ │ │ │ ├── BCGovAlertBanner.spec.js │ │ │ │ ├── BCGovFooter.spec.js │ │ │ │ ├── BCGovHeader.spec.js │ │ │ │ └── BCGovNavBar.spec.js │ │ │ ├── designer │ │ │ │ ├── FloatButton.spec.js │ │ │ │ ├── FormDesigner.spec.js │ │ │ │ ├── FormDisclaimer.spec.js │ │ │ │ ├── FormViewer.spec.js │ │ │ │ ├── FormViewerActions.spec.js │ │ │ │ ├── FormViewerMultiUpload.spec.js │ │ │ │ ├── FormsTable.spec.js │ │ │ │ ├── profile │ │ │ │ │ ├── FormAPIProfile.spec.js │ │ │ │ │ ├── FormDeploymentProfile.spec.js │ │ │ │ │ ├── FormLabelProfile.spec.js │ │ │ │ │ └── FormUseCaseProfile.spec.js │ │ │ │ └── settings │ │ │ │ │ ├── FormAccessSettings.spec.js │ │ │ │ │ ├── FormEventStreamSettings.spec.js │ │ │ │ │ ├── FormFunctionalitySettings.spec.js │ │ │ │ │ ├── FormMetadataSettings.spec.js │ │ │ │ │ ├── FormScheduleSettings.spec.js │ │ │ │ │ └── FormSubmissionSettings.spec.js │ │ │ ├── forms │ │ │ │ ├── ExportSubmissions.spec.js │ │ │ │ ├── FormSubmission.spec.js │ │ │ │ ├── PrintOptions.spec.js │ │ │ │ ├── RequestReceipt.spec.js │ │ │ │ ├── SubmissionsTable.spec.js │ │ │ │ ├── manage │ │ │ │ │ ├── AddTeamMember.spec.js │ │ │ │ │ ├── ApiKey.spec.js │ │ │ │ │ ├── DocumentTemplate.spec.js │ │ │ │ │ ├── EmailTemplate.spec.js │ │ │ │ │ ├── ManageForm.spec.js │ │ │ │ │ ├── ManageFormActions.spec.js │ │ │ │ │ ├── ManageLayout.spec.js │ │ │ │ │ ├── ManageVersions.spec.js │ │ │ │ │ ├── ShareForm.spec.js │ │ │ │ │ ├── Subscription.spec.js │ │ │ │ │ └── TeamManagement.spec.js │ │ │ │ └── submission │ │ │ │ │ ├── AuditHistory.spec.js │ │ │ │ │ ├── DeleteSubmission.spec.js │ │ │ │ │ ├── ManageSubmissionUsers.spec.js │ │ │ │ │ ├── MySubmissionsActions.spec.js │ │ │ │ │ ├── MySubmissionsTable.spec.js │ │ │ │ │ ├── NotesPanel.spec.js │ │ │ │ │ ├── StatusPanel.spec.js │ │ │ │ │ ├── StatusTable.spec.js │ │ │ │ │ ├── UserDuplicateSubmission.spec.js │ │ │ │ │ └── UserSubmission.spec.js │ │ │ └── infolinks │ │ │ │ ├── GeneralLayout.spec.js │ │ │ │ ├── ProactiveHelpDialog.spec.js │ │ │ │ └── ProactiveHelpPreviewDialog.spec.js │ │ │ ├── composables │ │ │ ├── documentTemplate.spec.js │ │ │ ├── form.spec.js │ │ │ └── printOptions.spec.js │ │ │ ├── filters │ │ │ └── index.spec.js │ │ │ ├── fixtures │ │ │ ├── form.json │ │ │ ├── identityProviders.json │ │ │ ├── idir_user.json │ │ │ ├── permissions.json │ │ │ └── roles.json │ │ │ ├── i18n.config.js │ │ │ ├── router.spec.js │ │ │ ├── services │ │ │ ├── adminService.spec.js │ │ │ ├── apiKeyService.spec.js │ │ │ ├── encryptionKeyService.spec.js │ │ │ ├── eventStreamConfigService.spec.js │ │ │ ├── formService.spec.js │ │ │ ├── rbacService.spec.js │ │ │ ├── roleService.spec.js │ │ │ └── userService.spec.js │ │ │ ├── setup.js │ │ │ ├── store │ │ │ └── modules │ │ │ │ ├── admin.actions.spec.js │ │ │ │ ├── auth.actions.spec.js │ │ │ │ ├── auth.getters.spec.js │ │ │ │ ├── form.actions.spec.js │ │ │ │ └── notifications.actions.spec.js │ │ │ ├── stubs.js │ │ │ ├── utils │ │ │ ├── constants.spec.js │ │ │ ├── permissionUtils.spec.js │ │ │ └── transformUtils.spec.js │ │ │ ├── views │ │ │ ├── About.spec.js │ │ │ ├── Admin.spec.js │ │ │ ├── Error.spec.js │ │ │ ├── Form.spec.js │ │ │ ├── Login.spec.js │ │ │ ├── NotFound.spec.js │ │ │ ├── User.spec.js │ │ │ ├── admin │ │ │ │ ├── Form.spec.js │ │ │ │ ├── Root.spec.js │ │ │ │ └── User.spec.js │ │ │ ├── file │ │ │ │ └── Download.spec.js │ │ │ ├── form │ │ │ │ ├── Create.spec.js │ │ │ │ ├── Design.spec.js │ │ │ │ ├── Export.spec.js │ │ │ │ ├── Manage.spec.js │ │ │ │ ├── Preview.spec.js │ │ │ │ ├── Submissions.spec.js │ │ │ │ ├── Submit.spec.js │ │ │ │ ├── Success.spec.js │ │ │ │ ├── Teams.spec.js │ │ │ │ └── View.spec.js │ │ │ └── user │ │ │ │ ├── Forms.spec.js │ │ │ │ ├── History.spec.js │ │ │ │ ├── Root.spec.js │ │ │ │ ├── SubmissionDraftEdit.spec.js │ │ │ │ ├── SubmissionDuplicate.spec.js │ │ │ │ ├── SubmissionView.spec.js │ │ │ │ └── Submissions.spec.js │ │ │ └── vuetify.config.js │ └── vite.config.mjs ├── jest.config.js ├── knexfile.js ├── lcov-fix.js ├── nodemon.json ├── package-lock.json ├── package.json ├── src │ ├── README.md │ ├── components │ │ ├── cdogsService.js │ │ ├── chesService.js │ │ ├── clamAvScanner.js │ │ ├── clientConnection.js │ │ ├── encryptionService.js │ │ ├── errorToProblem.js │ │ ├── eventStreamService.js │ │ ├── geoAddressService.js │ │ ├── idpService.js │ │ ├── jwtService.js │ │ └── log.js │ ├── db │ │ ├── .editorconfig │ │ ├── dataConnection.js │ │ ├── migrations │ │ │ ├── 20200807133615_000-init.js │ │ │ ├── 20200825155537_001-views.js │ │ │ ├── 20200825155537_002-idp_role_permission_data.js │ │ │ ├── 20200925103025_003-submission-users.js │ │ │ ├── 20201007112921_005-submissions-data-vw.js │ │ │ ├── 20201007113516_004-alter-form-view.js │ │ │ ├── 20201008142046_006-version-drafts.js │ │ │ ├── 20201019100738_007-public-submission-data-vw.js │ │ │ ├── 20201029141347_008-form-settings-notifications.js │ │ │ ├── 20201105120909_009-file-upload.js │ │ │ ├── 20210100113516_010-alter-form-view.js │ │ │ ├── 20210129120337_012-submission-status.js │ │ │ ├── 20210408120337_013-role-permission-for-owner.js │ │ │ ├── 20210426141115_014-form-submission-audit.js │ │ │ ├── 20210519170000_015-alter-submission-data-vw.js │ │ │ ├── 20210528134214_016-submission-submitters-vw copy.js │ │ │ ├── 20210614134214_017-alter-submission-submitters-add-form.js │ │ │ ├── 20210716000000_018-alter-submission-submitters-add-updatedAt.js │ │ │ ├── 20210721210234_019-api-key-models.js │ │ │ ├── 20210816000000_020-user-form-preferences.js │ │ │ ├── 20210830170000_021-alter-submissions-vw.js │ │ │ ├── 20210928000000_022-add-bceid-idps.js │ │ │ ├── 20211006000000_023-add-user-idps.js │ │ │ ├── 20211027000000_024-add-revising.js │ │ │ ├── 20221205215308_025-add-idp-guids.js │ │ │ ├── 20221208001614_026-remove-keycloak-id.js │ │ │ ├── 20230110063945_027-form_components_proactive_help.js │ │ │ ├── 20230110063945_028-alter-form-table-to-add-reminder-schedule-copy-col.js │ │ │ ├── 20230412141347_029-form-settings-uploadfile.js │ │ │ ├── 20230412181347_030-form-settings-uploadfile-form-view.js │ │ │ ├── 20230517012741_alter-user-form-access-view.js │ │ │ ├── 20230517012755_32-add-update-col-alter-submissions-vw.js │ │ │ ├── 20230618063952_033-form-subscribe.js │ │ │ ├── 20230705190020_change_file_storage_constrains.js │ │ │ ├── 20230818010845_add_modify_submissions_vw.js │ │ │ ├── 20230830164525_036-form-email-template.js │ │ │ ├── 20231017192656_037-user-form-permissions.js │ │ │ ├── 20231019153505_038-view-simplification.js │ │ │ ├── 20231121220415_039-form-profiling-data.js │ │ │ ├── 20231123172822_send-team-notification-email.js │ │ │ ├── 20240115201832_files-api-access.js │ │ │ ├── 20240312164557_remove-confirmationid-constraint.js │ │ │ ├── 20240321170550_wide-form-layout.js │ │ │ ├── 20240403192833_044-document-templates.js │ │ │ ├── 20240421000000_identity_provider_permissions.js │ │ │ ├── 20240423183912_045-add-approver-role.js │ │ │ ├── 20240521210143_046_external_api.js │ │ │ ├── 20240806171846_update_identity_provider_extra.js │ │ │ ├── 20240904140843_047-extend-submitter-role.js │ │ │ ├── 20240916104713_048-email-recipients.js │ │ │ ├── 20241010164117_049-update-idp-extra-length.js │ │ │ ├── 20241016164117__050-form-metadata.js │ │ │ ├── 20241016164117__051_modify_submissions_data_vw.js │ │ │ ├── 20241031164117_060_event_stream_service.js │ │ │ ├── 20241031164117_061_event_flags.js │ │ │ ├── 20241218233455_062_update_external_api_vw.js │ │ │ ├── 20241227201927_fix_identity_provider_extra.js │ │ │ ├── 20250213174458_idp_bcservicescard.js │ │ │ ├── 20250219000331_ess_allowlist.js │ │ │ ├── 20250319062555_alter-form-table-to-enable_team_member_draft_share.js │ │ │ ├── 20250407174410_idp_sort_order.js │ │ │ ├── 20250502163949_enable-bcsc-idp.js │ │ │ ├── 20250513152242_assigned_user_to_submissions_vw.js │ │ │ ├── 20250527190932_fix-username-idp-assignment-views.js │ │ │ ├── 20250528160202_assignee_to_submissions_data_vw.js │ │ │ └── 20250529210801_add-assignee-column-display-setting.js │ │ ├── seeds │ │ │ └── 999-dev-seed-data.js │ │ └── stamps.js │ ├── docs │ │ ├── .editorconfig │ │ ├── CHEFS_API_Documentation.postman_collection.json │ │ ├── docs.js │ │ └── v1.api-spec.yaml │ ├── forms │ │ ├── admin │ │ │ ├── controller.js │ │ │ ├── fileService.js │ │ │ ├── index.js │ │ │ ├── routes.js │ │ │ └── service.js │ │ ├── auth │ │ │ ├── middleware │ │ │ │ ├── apiAccess.js │ │ │ │ └── userAccess.js │ │ │ └── service.js │ │ ├── bcgeoaddress │ │ │ ├── controller.js │ │ │ ├── index.js │ │ │ ├── routes.js │ │ │ └── service.js │ │ ├── common │ │ │ ├── constants.js │ │ │ ├── middleware │ │ │ │ ├── errorHandler.js │ │ │ │ ├── index.js │ │ │ │ ├── rateLimiter.js │ │ │ │ └── validateParameter.js │ │ │ ├── models │ │ │ │ ├── index.js │ │ │ │ ├── jsonSchema.js │ │ │ │ ├── mixins.js │ │ │ │ ├── tables │ │ │ │ │ ├── documentTemplate.js │ │ │ │ │ ├── essAllowlist.js │ │ │ │ │ ├── externalAPI.js │ │ │ │ │ ├── externalAPIStatusCode.js │ │ │ │ │ ├── fileStorage.js │ │ │ │ │ ├── form.js │ │ │ │ │ ├── formApiKey.js │ │ │ │ │ ├── formComponentsProactiveHelp.js │ │ │ │ │ ├── formEmailTemplate.js │ │ │ │ │ ├── formEncryptionKey.js │ │ │ │ │ ├── formEventStreamConfig.js │ │ │ │ │ ├── formIdentityProvider.js │ │ │ │ │ ├── formMetadata.js │ │ │ │ │ ├── formRoleUser.js │ │ │ │ │ ├── formStatusCode.js │ │ │ │ │ ├── formSubmission.js │ │ │ │ │ ├── formSubmissionStatus.js │ │ │ │ │ ├── formSubmissionUser.js │ │ │ │ │ ├── formSubscription.js │ │ │ │ │ ├── formVersion.js │ │ │ │ │ ├── formVersionDraft.js │ │ │ │ │ ├── identityProvider.js │ │ │ │ │ ├── label.js │ │ │ │ │ ├── note.js │ │ │ │ │ ├── permission.js │ │ │ │ │ ├── role.js │ │ │ │ │ ├── statusCode.js │ │ │ │ │ ├── submissionAudit.js │ │ │ │ │ ├── user.js │ │ │ │ │ └── userFormPreferences.js │ │ │ │ ├── utils.js │ │ │ │ └── views │ │ │ │ │ ├── adminExternalAPI.js │ │ │ │ │ ├── formSubmissionUserPermissions.js │ │ │ │ │ ├── publicFormAccess.js │ │ │ │ │ ├── submissionData.js │ │ │ │ │ ├── submissionMetadata.js │ │ │ │ │ ├── userFormAccess.js │ │ │ │ │ └── userSubmissions.js │ │ │ ├── scheduleService.js │ │ │ └── utils.js │ │ ├── email │ │ │ ├── assets │ │ │ │ ├── bodies │ │ │ │ │ ├── file-download-ready.html │ │ │ │ │ ├── reminder-form-not-fill.html │ │ │ │ │ ├── reminder-form-open.html │ │ │ │ │ ├── reminder-form-will-close.html │ │ │ │ │ ├── send-status-assigned-email-body.html │ │ │ │ │ ├── send-status-revising-email-body.html │ │ │ │ │ ├── submission-assigned.html │ │ │ │ │ ├── submission-completed.html │ │ │ │ │ ├── submission-confirmation.html │ │ │ │ │ ├── submission-received-confirmation-login.html │ │ │ │ │ ├── submission-received-confirmation-public.html │ │ │ │ │ └── submission-unassigned.html │ │ │ │ └── triggered-notification-email-template.html │ │ │ ├── emailService.js │ │ │ └── reminderService.js │ │ ├── event │ │ │ └── eventService.js │ │ ├── file │ │ │ ├── controller.js │ │ │ ├── index.js │ │ │ ├── middleware │ │ │ │ ├── filePermissions.js │ │ │ │ ├── upload.js │ │ │ │ └── virusScan.js │ │ │ ├── routes.js │ │ │ ├── service.js │ │ │ └── storage │ │ │ │ ├── localStorageService.js │ │ │ │ ├── objectStorageService.js │ │ │ │ └── storageService.js │ │ ├── form │ │ │ ├── controller.js │ │ │ ├── encryptionKey │ │ │ │ ├── controller.js │ │ │ │ ├── routes.js │ │ │ │ └── service.js │ │ │ ├── eventStreamConfig │ │ │ │ ├── controller.js │ │ │ │ ├── routes.js │ │ │ │ └── service.js │ │ │ ├── exportService.js │ │ │ ├── externalApi │ │ │ │ ├── controller.js │ │ │ │ ├── index.js │ │ │ │ ├── routes.js │ │ │ │ └── service.js │ │ │ ├── formMetadata │ │ │ │ └── service.js │ │ │ ├── index.js │ │ │ ├── routes.js │ │ │ └── service.js │ │ ├── permission │ │ │ ├── controller.js │ │ │ ├── index.js │ │ │ ├── routes.js │ │ │ └── service.js │ │ ├── proxy │ │ │ ├── controller.js │ │ │ ├── error.js │ │ │ ├── index.js │ │ │ ├── routes.js │ │ │ └── service.js │ │ ├── public │ │ │ ├── controller.js │ │ │ ├── index.js │ │ │ ├── middleware │ │ │ │ └── apiAccess.js │ │ │ ├── routes.js │ │ │ └── service.js │ │ ├── rbac │ │ │ ├── controller.js │ │ │ ├── index.js │ │ │ ├── routes.js │ │ │ └── service.js │ │ ├── role │ │ │ ├── controller.js │ │ │ ├── index.js │ │ │ ├── routes.js │ │ │ └── service.js │ │ ├── submission │ │ │ ├── controller.js │ │ │ ├── index.js │ │ │ ├── routes.js │ │ │ └── service.js │ │ ├── user │ │ │ ├── controller.js │ │ │ ├── index.js │ │ │ ├── routes.js │ │ │ └── service.js │ │ └── utils │ │ │ ├── index.js │ │ │ └── routes.js │ └── routes │ │ └── v1.js └── tests │ ├── common │ ├── dbHelper.js │ └── helper.js │ ├── fixtures │ ├── form │ │ ├── Kitchen_sink_form_schema_datagrid.json │ │ ├── advanced_schema.json │ │ ├── fields │ │ │ └── extractedFields.json │ │ ├── identity_providers.json │ │ ├── kitchen_sink_form_schema_multiple_component_test.json │ │ └── kitchen_sink_schema.json │ └── submission │ │ ├── advanced_submission.json │ │ ├── advanced_submissions_export.json │ │ ├── kitchen_sink_submission.json │ │ ├── kitchen_sink_submission_data_export_datagrid.json │ │ ├── kitchen_sink_submission_data_export_datagrid_fields_selection.json │ │ ├── kitchen_sink_submission_data_multiple_component_test.json │ │ ├── kitchen_sink_submission_extract_field_csv_export.json │ │ ├── kitchen_sink_submission_pagination.json │ │ └── kitchen_sink_submissions_export.json │ └── unit │ ├── README.md │ ├── components │ ├── cdogsService.spec.js │ ├── clamAvScanner.spec.js │ ├── encryptionService.spec.js │ ├── errorToProblem.spec.js │ ├── eventStreamService.spec.js │ ├── idpService.spec.js │ ├── jwtService.spec.js │ └── log.spec.js │ ├── forms │ ├── admin │ │ ├── controller.spec.js │ │ ├── routes.spec.js │ │ └── service.spec.js │ ├── auth │ │ ├── authService.spec.js │ │ └── middleware │ │ │ ├── apiAccess.spec.js │ │ │ └── userAccess.spec.js │ ├── bcgeoaddress │ │ ├── controller.spec.js │ │ ├── routes.spec.js │ │ └── service.spec.js │ ├── common │ │ ├── middleware │ │ │ ├── errorHandler.spec.js │ │ │ ├── rateLimiter.spec.js │ │ │ └── validateParameter.spec.js │ │ ├── models │ │ │ └── utils.spec.js │ │ ├── scheduleService.spec.js │ │ └── utils.spec.js │ ├── email │ │ ├── emailService.spec.js │ │ └── reminderService.spec.js │ ├── file │ │ ├── controller.spec.js │ │ ├── middleware │ │ │ ├── filePermissions.spec.js │ │ │ ├── upload.spec.js │ │ │ └── virusScan.spec.js │ │ └── routes.spec.js │ ├── form │ │ ├── controller.spec.js │ │ ├── encryptionKey │ │ │ ├── controller.spec.js │ │ │ ├── routes.spec.js │ │ │ └── service.spec.js │ │ ├── eventStreamConfig │ │ │ ├── controller.spec.js │ │ │ ├── routes.spec.js │ │ │ └── service.spec.js │ │ ├── exportService.spec.js │ │ ├── externalApi │ │ │ ├── controller.spec.js │ │ │ ├── routes.spec.js │ │ │ └── service.spec.js │ │ ├── formMetadata │ │ │ └── service.spec.js │ │ ├── routes.spec.js │ │ └── service.spec.js │ ├── permission │ │ └── routes.spec.js │ ├── proxy │ │ ├── controller.spec.js │ │ ├── routes.spec.js │ │ └── service.spec.js │ ├── public │ │ ├── middleware │ │ │ └── apiAccess.spec.js │ │ └── routes.spec.js │ ├── rbac │ │ ├── controller.spec.js │ │ ├── routes.spec.js │ │ └── service.spec.js │ ├── role │ │ └── routes.spec.js │ ├── submission │ │ ├── controller.spec.js │ │ ├── routes.spec.js │ │ └── service.spec.js │ ├── user │ │ ├── routes.spec.js │ │ └── service.spec.js │ └── utils │ │ └── routes.spec.js │ └── routes │ ├── v1.spec.js │ └── v1 │ ├── admin.spec.js │ ├── form.spec.js │ ├── permission.spec.js │ ├── rbac.spec.js │ ├── role.spec.js │ ├── submission.spec.js │ └── user.spec.js ├── clamav ├── .dockerignore ├── Dockerfile ├── README.md ├── charts │ └── clamav │ │ ├── .helmignore │ │ ├── Chart.yaml │ │ ├── README.md │ │ ├── templates │ │ ├── _helpers.tpl │ │ ├── configmap.yaml │ │ ├── deployment.yaml │ │ ├── nsp.yaml │ │ ├── pdb.yaml │ │ └── service.yaml │ │ ├── values-dev.yaml │ │ ├── values-test.yaml │ │ └── values.yaml ├── clamdcheck.sh └── config │ ├── clamd.conf │ └── freshclam.conf ├── components ├── .codeclimate.yml ├── .gitignore ├── .prettierrc ├── README.md ├── gulpfile.js ├── package-lock.json ├── package.json ├── src │ ├── components │ │ ├── BCAddress │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Address.edit.provider.ts │ │ ├── Common │ │ │ ├── Advanced.edit.api.ts │ │ │ ├── Advanced.edit.conditional.ts │ │ │ ├── Advanced.edit.data.ts │ │ │ ├── Advanced.edit.display.ts │ │ │ ├── Advanced.edit.layout.ts │ │ │ ├── Advanced.edit.logic.ts │ │ │ ├── Advanced.edit.validation.ts │ │ │ ├── Constants.ts │ │ │ ├── Evaluator.ts │ │ │ ├── Simple.edit.api.ts │ │ │ ├── Simple.edit.conditional.ts │ │ │ ├── Simple.edit.data.ts │ │ │ ├── Simple.edit.display.ts │ │ │ ├── Simple.edit.validation.ts │ │ │ ├── UseForCopy.ts │ │ │ ├── function.ts │ │ │ └── utils.ts │ │ ├── Map │ │ │ ├── Common │ │ │ │ ├── Constants.d.ts │ │ │ │ ├── Constants.js │ │ │ │ ├── MapConstants.ts │ │ │ │ └── marker-icon.png │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ ├── editForm │ │ │ │ ├── Component.edit.data.ts │ │ │ │ ├── Component.edit.display.ts │ │ │ │ └── Component.edit.validation.ts │ │ │ └── services │ │ │ │ ├── BCGeocoderProvider.ts │ │ │ │ └── MapService.ts │ │ ├── OrgBook │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ ├── Orgbook.edit.data.ts │ │ │ │ ├── Orgbook.edit.display.ts │ │ │ │ └── Orgbook.edit.validation.ts │ │ ├── SimpleAddressAdvanced │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleBCAddress │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Address.edit.provider.ts │ │ ├── SimpleButtonAdvanced │ │ │ ├── Component.form.ts │ │ │ └── Component.ts │ │ ├── SimpleButtonReset │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.display.ts │ │ ├── SimpleButtonSubmit │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.display.ts │ │ ├── SimpleCheckbox │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ ├── Component.edit.display.ts │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleCheckboxAdvanced │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleCheckboxes │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ ├── Component.edit.data.ts │ │ │ │ ├── Component.edit.display.ts │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleColumns2 │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.display.ts │ │ ├── SimpleColumns3 │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.display.ts │ │ ├── SimpleColumns4 │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.display.ts │ │ ├── SimpleContent │ │ │ ├── Component.form.ts │ │ │ └── Component.ts │ │ ├── SimpleCurrencyAdvanced │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleDateTime │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ ├── Component.edit.data.ts │ │ │ │ ├── Component.edit.date.ts │ │ │ │ ├── Component.edit.display.ts │ │ │ │ └── Component.edit.time.ts │ │ ├── SimpleDateTimeAdvanced │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleDay │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ ├── Component.edit.data.ts │ │ │ │ ├── Component.edit.day.ts │ │ │ │ ├── Component.edit.display.ts │ │ │ │ ├── Component.edit.month.ts │ │ │ │ ├── Component.edit.validation.ts │ │ │ │ └── Component.edit.year.ts │ │ ├── SimpleDayAdvanced │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleEmail │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ ├── Component.edit.data.ts │ │ │ │ └── Component.edit.display.ts │ │ ├── SimpleEmailAdvanced │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleFieldSet │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.display.ts │ │ ├── SimpleFile │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ ├── Component.edit.data.ts │ │ │ │ ├── Component.edit.display.ts │ │ │ │ └── Component.edit.file.ts │ │ ├── SimpleHeading │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.display.ts │ │ ├── SimpleNumber │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ ├── Component.edit.data.ts │ │ │ │ ├── Component.edit.display.ts │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleNumberAdvanced │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimplePanel │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.display.ts │ │ ├── SimpleParagraph │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.display.ts │ │ ├── SimplePasswordAdvanced │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimplePhoneNumber │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ ├── Component.edit.data.ts │ │ │ │ └── Component.edit.display.ts │ │ ├── SimplePhoneNumberAdvanced │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleRadioAdvanced │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleRadios │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ ├── Component.edit.data.ts │ │ │ │ ├── Component.edit.display.ts │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleSelect │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ ├── Component.edit.data.ts │ │ │ │ ├── Component.edit.display.ts │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleSelectAdvanced │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleSelectBoxesAdvanced │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleSignatureAdvanced │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleSurveyAdvanced │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleTabs │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.display.ts │ │ ├── SimpleTagsAdvanced │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleTextArea │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ ├── Component.edit.data.ts │ │ │ │ ├── Component.edit.display.ts │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleTextAreaAdvanced │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleTextField │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ ├── Component.edit.data.ts │ │ │ │ ├── Component.edit.display.ts │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleTextFieldAdvanced │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ ├── Component.edit.display.ts │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleTime │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ ├── Component.edit.data.ts │ │ │ │ └── Component.edit.display.ts │ │ ├── SimpleTimeAdvanced │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.validation.ts │ │ ├── SimpleUrlAdvanced │ │ │ ├── Component.form.ts │ │ │ ├── Component.ts │ │ │ └── editForm │ │ │ │ └── Component.edit.validation.ts │ │ └── index.ts │ ├── ejs.d.ts │ ├── index.ts │ ├── overrides │ │ └── editform │ │ │ └── utils.ts │ ├── sass │ │ └── contrib.scss │ └── use.ts ├── tsconfig.json ├── tslint.json ├── webpack.config.js ├── webpack.prod.js └── webpack.use.js ├── openshift ├── README.md ├── allow-from-openshift-ingress.np.yaml ├── app.cm.yaml ├── app.cronjob.yaml ├── app.deployment.yaml ├── app.dev.param ├── app.prod.param ├── app.secret.yaml ├── app.test.param ├── clamav.cm.yaml ├── clamav.dev.param ├── clamav.prod.param ├── clamav.test.param ├── crunchydb │ ├── README.md │ └── charts │ │ ├── crunchy-postgres │ │ ├── .helmignore │ │ ├── Chart.yaml │ │ ├── README.md │ │ ├── templates │ │ │ ├── PostgresCluster.yaml │ │ │ ├── _helpers.tpl │ │ │ ├── _s3.tpl │ │ │ └── s3Secret.yaml │ │ └── values.yaml │ │ └── tools │ │ ├── .helmignore │ │ ├── Chart.yaml │ │ ├── README.md │ │ ├── templates │ │ ├── _helpers.tpl │ │ ├── deployer │ │ │ ├── deployerRole.yaml │ │ │ ├── deployerRoleBinding.yaml │ │ │ └── deployerServiceAccount.yaml │ │ ├── linter │ │ │ ├── linterRole.yaml │ │ │ ├── linterRoleBinding.yaml │ │ │ └── linterServiceAccount.yaml │ │ ├── networking │ │ │ ├── networkPolicy.yaml │ │ │ ├── podNetworkPolicy.yaml │ │ │ └── route.yaml │ │ └── provisioner │ │ │ ├── provisionerRole.yaml │ │ │ ├── provisionerRoleBinding.yaml │ │ │ └── provisionerServiceAccount.yaml │ │ └── values.yaml ├── ess.cm.yaml ├── ess.dev.param ├── ess.prod.param ├── ess.test.param ├── patroni-ephemeral.dc.yaml ├── patroni.dc.yaml ├── patroni.dev.param ├── patroni.prod.param ├── patroni.secret.yaml ├── patroni.test.param └── redash │ ├── README.md │ ├── backup-cronjob-verify.yaml │ ├── crunchydb-postgres-values-no-limits.yaml │ ├── crunchydb-postgres-values.yaml │ ├── crunchydb-tools-values.yaml │ ├── patroni.networkpolicy.yaml │ ├── postgresql.deploy.yaml │ ├── redash.route.yaml │ ├── redis.deploy.yaml │ └── secrets.yaml └── tests ├── functional └── cypress │ ├── .eslintrc.js │ ├── README.md │ ├── cypress.config.js │ ├── e2e │ ├── about.cy.js │ ├── form-apikey-cdogs.cy.js │ ├── form-design-advanceddata.cy.js │ ├── form-design-advancedfield.cy.js │ ├── form-design-basicfields.cy.js │ ├── form-design-basiclayout-advanced-layout.cy.js │ ├── form-design-export-import-design.cy.js │ ├── form-design-map-point-circle-markers.cy.js │ ├── form-design-map-polygon-marker.cy.js │ ├── form-draft-submission-management.cy.js │ ├── form-edit-submission-data.cy.js │ ├── form-manage-form.cy.js │ ├── form-simple-form-publish.cy.js │ ├── form-submission-assign-revise-status.cy.js │ ├── form-submission-assign-status.cy.js │ ├── form-submission-export.cy.js │ ├── form-submission-public-no-status-assign.cy.js │ ├── form-submission-public-status-assign.cy.js │ ├── form-team-email-management.cy.js │ └── kitchen-sink.cy.js │ ├── fixtures │ ├── SamplePPTx.pptx │ ├── Testing_files.txt │ ├── add1.png │ ├── file_example_XLSX_50.xlsx │ ├── formInitialBuilder │ │ ├── add1.png │ │ ├── ccHelpLinkInfoList.json │ │ └── formBuilderOptions.json │ ├── formModules │ │ ├── activeFormModules.json │ │ └── formModuleVersion.json │ ├── forms │ │ ├── current-forms.json │ │ ├── empty-array.json │ │ ├── form-field.json │ │ ├── form-submission-deleted.json │ │ ├── form-submission.json │ │ ├── form.json │ │ ├── griddata.json │ │ └── submission-restore.json │ ├── kitchensink │ │ ├── formModuleVersions.json │ │ ├── formOptions.json │ │ ├── formVersion.json │ │ ├── submission.json │ │ └── submissionOptions.json │ ├── test.docx │ ├── test_schema.json │ └── users │ │ └── user.json │ ├── package-lock.json │ ├── package.json │ ├── plugins │ └── index.js │ └── support │ ├── commands.js │ ├── component.js │ ├── e2e.js │ ├── index.js │ └── login.js └── performance ├── .sample-env ├── README.md ├── backend.js ├── common ├── auth.js └── params.js ├── fixtures ├── forms │ ├── 01_kitchen_sink_advanced.json │ └── 02_kitchen_sink_no_sig.json ├── schemas │ ├── 01_kitchen_sink_advanced.json │ └── 02_kitchen_sink_no_sig.json └── submissions │ ├── 01_kitchen_sink_advanced.json │ └── 02_kitchen_sink_no_sig.json └── tests └── backend └── forms.js /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG VARIANT="20.18.3-bookworm" 2 | FROM node:${VARIANT} 3 | 4 | # Install some extras such as vim for interactive rebases. Also some 5 | # Cypress prerequisites for running in Debian containers: 6 | # https://docs.cypress.io/app/get-started/install-cypress#UbuntuDebian 7 | # and we set up k6 for performance testing 8 | RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69 && \ 9 | echo "deb https://dl.k6.io/deb stable main" | tee /etc/apt/sources.list.d/k6.list && \ 10 | apt-get update && \ 11 | DEBIAN_FRONTEND=noninteractive apt-get install -y \ 12 | k6 \ 13 | libasound2 \ 14 | libgbm-dev \ 15 | libgtk-3-0 \ 16 | libgtk2.0-0 \ 17 | libnotify-dev \ 18 | libnss3 \ 19 | libxss1 \ 20 | libxtst6 \ 21 | vim \ 22 | xauth \ 23 | xvfb \ 24 | && apt-get clean 25 | -------------------------------------------------------------------------------- /.devcontainer/README.md: -------------------------------------------------------------------------------- 1 | # CHEFS Development with Dev Container 2 | 3 | Please refer to the online [CHEFS Techdocs](https://developer.gov.bc.ca/docs/default/component/chefs-techdocs/): 4 | 5 | - [CHEFS Development with Dev Container](https://developer.gov.bc.ca/docs/default/component/chefs-techdocs/Developer/Contributors/CHEFS-Development-with-Dev-Container/) 6 | - [Local Setup](https://developer.gov.bc.ca/docs/default/component/chefs-techdocs/Developer/Contributors/Local-Setup/) 7 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Editor directories and files 2 | .DS_Store 3 | .gradle 4 | .nyc_output 5 | .scannerwork 6 | build 7 | coverage 8 | dist 9 | files 10 | **/e2e/videos 11 | node_modules 12 | # Ignore only top-level package-lock.json 13 | /package-lock.json 14 | 15 | # Ignore Helm subcharts 16 | charts/**/charts 17 | Chart.lock 18 | 19 | # local env files 20 | local.* 21 | local-*.* 22 | .env.local 23 | .env.*.local 24 | 25 | # Log files 26 | npm-debug.log* 27 | yarn-debug.log* 28 | yarn-error.log* 29 | 30 | # Editor directories and files 31 | .idea 32 | .vscode 33 | *.iml 34 | *.suo 35 | *.ntvs* 36 | *.njsproj 37 | *.sln 38 | *.sw? 39 | *.mp4 40 | 41 | # temp office files 42 | ~$* 43 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | 9 | [*.html] 10 | indent_style = space 11 | indent_size = 2 12 | 13 | [*.{css,js,json,jsx,scss,ts,tsx,vue}] 14 | indent_style = space 15 | indent_size = 2 16 | 17 | [.{babelrc,eslintrc}] 18 | indent_style = space 19 | indent_size = 2 20 | 21 | # ignore patterns 22 | [*.{gif,ico}] 23 | end_of_line = unset 24 | insert_final_newline = unset 25 | trim_trailing_whitespace = unset 26 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Autodetect text files and forces unix eols, so Windows does not break them 2 | * text=auto eol=lf 3 | 4 | # Force images/fonts to be handled as binaries 5 | *.jpg binary 6 | *.jpeg binary 7 | *.gif binary 8 | *.png binary 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | #### Is your feature request related to a problem? Please describe 11 | 12 | 13 | 14 | #### Describe the solution you'd like 15 | 16 | 17 | 18 | #### Describe alternatives you've considered 19 | 20 | 21 | 22 | #### Additional context 23 | 24 | 25 | -------------------------------------------------------------------------------- /.github/workflows/reusable-owasp-zap.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Reusable workflow to run the OWASP ZAP (Open Worldwide Application Security 3 | # Project - Zed Attack Proxy) Scan against a deployed application. 4 | # 5 | name: OWASP ZAP Scan 6 | on: 7 | workflow_call: 8 | inputs: 9 | url: 10 | required: true 11 | type: string 12 | 13 | jobs: 14 | owasp-zap: 15 | name: OWASP ZAP Scan 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Run Scan 20 | uses: zaproxy/action-full-scan@v0.12.0 21 | with: 22 | # Do not create GitHub Issues 23 | allow_issue_writing: false 24 | 25 | artifact_name: OWASP ZAP Scan 26 | 27 | # -a: include the alpha passive scan rules as well 28 | # -d: show debug messages 29 | cmd_options: "-a -d" 30 | 31 | target: ${{ inputs.url }} 32 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | bcgov-citz-ccft.github.ca -------------------------------------------------------------------------------- /COMPLIANCE.yaml: -------------------------------------------------------------------------------- 1 | name: compliance 2 | description: | 3 | This document is used to track a projects PIA and STRA 4 | compliance. 5 | spec: 6 | - name: PIA 7 | status: COMPLETED 8 | last-updated: "2023-03-17T00:00:00.000Z" 9 | - name: STRA 10 | status: COMPLETED 11 | last-updated: "2023-08-04T00:00:00.000Z" 12 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker.io/node:20.18.3-alpine3.21 2 | ENV NO_UPDATE_NOTIFIER=true 3 | WORKDIR /opt/app-root/src/app 4 | COPY . /opt/app-root/src 5 | 6 | # Run the npm tasks to set up the various parts of the application. Then create 7 | # the /.npm directory and grant access to group 0 to allow npm v9 to work 8 | # See: https://docs.openshift.com/container-platform/4.11/openshift_images/create-images.html#use-uid_create-images 9 | 10 | RUN npm run all:ci \ 11 | && npm run all:build \ 12 | && npm run frontend:purge \ 13 | && npm run components:clean \ 14 | && npm run components:purge \ 15 | && mkdir /.npm \ 16 | && chgrp -R 0 /.npm \ 17 | && chmod -R g=u /.npm 18 | 19 | EXPOSE 8000 20 | 21 | CMD ["npm", "run", "start"] 22 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman 2 | -------------------------------------------------------------------------------- /app/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | 9 | [*.html] 10 | indent_style = space 11 | indent_size = 2 12 | 13 | [*.{css,js,json,jsx,scss,ts,tsx,vue}] 14 | indent_style = space 15 | indent_size = 2 16 | 17 | [.{babelrc,eslintrc}] 18 | indent_style = space 19 | indent_size = 2 20 | -------------------------------------------------------------------------------- /app/.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | frontend 3 | node_modules 4 | public/js 5 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | myfiles 2 | myfiles/* 3 | -------------------------------------------------------------------------------- /app/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | frontend 3 | node_modules 4 | public/js 5 | src/db/* -------------------------------------------------------------------------------- /app/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "printWidth": 180, 5 | "tabWidth": 2 6 | } 7 | -------------------------------------------------------------------------------- /app/config/production.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /app/frontend/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not ie <= 8 4 | -------------------------------------------------------------------------------- /app/frontend/.eslintignore: -------------------------------------------------------------------------------- 1 | coverage 2 | dist 3 | node_modules 4 | public/js 5 | src/formio 6 | tests -------------------------------------------------------------------------------- /app/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | .idea 18 | .DS_Store 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | 25 | .env 26 | .env.development 27 | .env.production 28 | .env* 29 | .vscode -------------------------------------------------------------------------------- /app/frontend/.prettierignore: -------------------------------------------------------------------------------- 1 | tests -------------------------------------------------------------------------------- /app/frontend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "printWidth": 80 5 | } 6 | -------------------------------------------------------------------------------- /app/frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Common Hosted Forms Service 9 | 10 | 11 | 12 | 13 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/frontend/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "allowJs": true 3 | } 4 | -------------------------------------------------------------------------------- /app/frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcgov/common-hosted-form-service/ee209435541c048da1bc081f42fd5008ae415c06/app/frontend/public/favicon.ico -------------------------------------------------------------------------------- /app/frontend/public/images/drag_drop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcgov/common-hosted-form-service/ee209435541c048da1bc081f42fd5008ae415c06/app/frontend/public/images/drag_drop.png -------------------------------------------------------------------------------- /app/frontend/public/images/quickstart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcgov/common-hosted-form-service/ee209435541c048da1bc081f42fd5008ae415c06/app/frontend/public/images/quickstart.png -------------------------------------------------------------------------------- /app/frontend/public/images/team-management.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcgov/common-hosted-form-service/ee209435541c048da1bc081f42fd5008ae415c06/app/frontend/public/images/team-management.png -------------------------------------------------------------------------------- /app/frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <%= process.env.VUE_APP_TITLE %> 10 | 11 | 12 | 13 | 17 |
18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/frontend/src/assets/images/drag-drop-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcgov/common-hosted-form-service/ee209435541c048da1bc081f42fd5008ae415c06/app/frontend/src/assets/images/drag-drop-demo.gif -------------------------------------------------------------------------------- /app/frontend/src/components/admin/AdministerUser.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 33 | -------------------------------------------------------------------------------- /app/frontend/src/components/admin/Dashboard.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 15 | -------------------------------------------------------------------------------- /app/frontend/src/components/base/BaseInfoCard.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 33 | -------------------------------------------------------------------------------- /app/frontend/src/components/base/BaseNotificationContainer.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | 20 | 30 | -------------------------------------------------------------------------------- /app/frontend/src/components/base/BasePanel.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 30 | -------------------------------------------------------------------------------- /app/frontend/src/components/base/BasePrintButton.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | -------------------------------------------------------------------------------- /app/frontend/src/composables/printOptions.js: -------------------------------------------------------------------------------- 1 | export function createDownload(blob, filename = undefined) { 2 | const url = window.URL.createObjectURL(blob); 3 | const a = document.createElement('a'); 4 | a.style.display = 'none'; 5 | a.href = url; 6 | a.download = filename; 7 | a.click(); 8 | window.URL.revokeObjectURL(url); 9 | a.remove(); 10 | } 11 | -------------------------------------------------------------------------------- /app/frontend/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcgov/common-hosted-form-service/ee209435541c048da1bc081f42fd5008ae415c06/app/frontend/src/favicon.ico -------------------------------------------------------------------------------- /app/frontend/src/filters/index.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | 3 | // 4 | // Date format Filters {{ expression | filter }} 5 | // 6 | 7 | /** 8 | * @function formatDate 9 | * Converts a date to an 'MMMM D YYYY' formatted string 10 | * @param {Date} value A date object 11 | * @returns {String} A string representation of `value` 12 | */ 13 | export function formatDate(value) { 14 | if (value) { 15 | return moment(String(value)).format('MMMM D YYYY'); 16 | } 17 | } 18 | 19 | /** 20 | * @function formatDateLong 21 | * Converts a date to a 'YYYY-MM-DD hh:mm:ss a' formatted string 22 | * @param {Date} value A date object 23 | * @returns {String} A string representation of `value` 24 | */ 25 | export function formatDateLong(value) { 26 | if (value) { 27 | return moment(String(value)).format('YYYY-MM-DD hh:mm:ss a'); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/frontend/src/internationalization/trans/chefs/ar/index.js: -------------------------------------------------------------------------------- 1 | import ar from '~/internationalization/trans/chefs/ar/ar.json'; 2 | export default { 3 | trans: ar, 4 | }; 5 | -------------------------------------------------------------------------------- /app/frontend/src/internationalization/trans/chefs/de/index.js: -------------------------------------------------------------------------------- 1 | import de from '~/internationalization/trans/chefs/de/de.json'; 2 | export default { 3 | trans: de, 4 | }; 5 | -------------------------------------------------------------------------------- /app/frontend/src/internationalization/trans/chefs/en/index.js: -------------------------------------------------------------------------------- 1 | import en from '~/internationalization/trans/chefs/en/en.json'; 2 | export default { 3 | trans: en, 4 | }; 5 | -------------------------------------------------------------------------------- /app/frontend/src/internationalization/trans/chefs/es/index.js: -------------------------------------------------------------------------------- 1 | import es from '~/internationalization/trans/chefs/es/es.json'; 2 | export default { 3 | trans: es, 4 | }; 5 | -------------------------------------------------------------------------------- /app/frontend/src/internationalization/trans/chefs/fa/index.js: -------------------------------------------------------------------------------- 1 | import fa from '~/internationalization/trans/chefs/fa/fa.json'; 2 | export default { 3 | trans: fa, 4 | }; 5 | -------------------------------------------------------------------------------- /app/frontend/src/internationalization/trans/chefs/fr/index.js: -------------------------------------------------------------------------------- 1 | import fr from '~/internationalization/trans/chefs/fr/fr.json'; 2 | export default { 3 | trans: fr, 4 | }; 5 | -------------------------------------------------------------------------------- /app/frontend/src/internationalization/trans/chefs/hi/index.js: -------------------------------------------------------------------------------- 1 | import hi from '~/internationalization/trans/chefs/hi/hi.json'; 2 | export default { 3 | trans: hi, 4 | }; 5 | -------------------------------------------------------------------------------- /app/frontend/src/internationalization/trans/chefs/it/index.js: -------------------------------------------------------------------------------- 1 | import it from '~/internationalization/trans/chefs/it/it.json'; 2 | export default { 3 | trans: it, 4 | }; 5 | -------------------------------------------------------------------------------- /app/frontend/src/internationalization/trans/chefs/ja/index.js: -------------------------------------------------------------------------------- 1 | import ja from '~/internationalization/trans/chefs/ja/ja.json'; 2 | export default { 3 | trans: ja, 4 | }; 5 | -------------------------------------------------------------------------------- /app/frontend/src/internationalization/trans/chefs/ko/index.js: -------------------------------------------------------------------------------- 1 | import ko from '~/internationalization/trans/chefs/ko/ko.json'; 2 | export default { 3 | trans: ko, 4 | }; 5 | -------------------------------------------------------------------------------- /app/frontend/src/internationalization/trans/chefs/pa/index.js: -------------------------------------------------------------------------------- 1 | import pa from '~/internationalization/trans/chefs/pa/pa.json'; 2 | export default { 3 | trans: pa, 4 | }; 5 | -------------------------------------------------------------------------------- /app/frontend/src/internationalization/trans/chefs/pt/index.js: -------------------------------------------------------------------------------- 1 | import pt from '~/internationalization/trans/chefs/pt/pt.json'; 2 | export default { 3 | trans: pt, 4 | }; 5 | -------------------------------------------------------------------------------- /app/frontend/src/internationalization/trans/chefs/ru/index.js: -------------------------------------------------------------------------------- 1 | import ru from '~/internationalization/trans/chefs/ru/ru.json'; 2 | export default { 3 | trans: ru, 4 | }; 5 | -------------------------------------------------------------------------------- /app/frontend/src/internationalization/trans/chefs/tl/index.js: -------------------------------------------------------------------------------- 1 | import tl from '~/internationalization/trans/chefs/tl/tl.json'; 2 | export default { 3 | trans: tl, 4 | }; 5 | -------------------------------------------------------------------------------- /app/frontend/src/internationalization/trans/chefs/uk/index.js: -------------------------------------------------------------------------------- 1 | import uk from '~/internationalization/trans/chefs/uk/uk.json'; 2 | export default { 3 | trans: uk, 4 | }; 5 | -------------------------------------------------------------------------------- /app/frontend/src/internationalization/trans/chefs/vi/index.js: -------------------------------------------------------------------------------- 1 | import vi from '~/internationalization/trans/chefs/vi/vi.json'; 2 | export default { 3 | trans: vi, 4 | }; 5 | -------------------------------------------------------------------------------- /app/frontend/src/internationalization/trans/chefs/zh/index.js: -------------------------------------------------------------------------------- 1 | import zh from '~/internationalization/trans/chefs/zh/zh.json'; 2 | export default { 3 | trans: zh, 4 | }; 5 | -------------------------------------------------------------------------------- /app/frontend/src/internationalization/trans/chefs/zhTW/index.js: -------------------------------------------------------------------------------- 1 | import zhTW from '~/internationalization/trans/chefs/zhTW/zh-TW.json'; 2 | export default { 3 | trans: zhTW, 4 | }; 5 | -------------------------------------------------------------------------------- /app/frontend/src/internationalization/trans/vuetify/locale/README.md: -------------------------------------------------------------------------------- 1 | This directory contains the translation files for languages that are supported 2 | by CHEFS, but not supported by Vuetify. 3 | -------------------------------------------------------------------------------- /app/frontend/src/services/eventStreamConfigService.js: -------------------------------------------------------------------------------- 1 | import { appAxios } from '~/services/interceptors'; 2 | import { ApiRoutes } from '~/utils/constants'; 3 | 4 | export default { 5 | /** 6 | * @function getEventStreamConfig 7 | * Get the event stream configuration 8 | * @param {string} formId The form uuid 9 | * @param {Object} [params={}] The query parameters 10 | * @returns {Promise} An axios response 11 | */ 12 | getEventStreamConfig(formId, params = {}) { 13 | return appAxios().get( 14 | `${ApiRoutes.FORMS}/${formId}${ApiRoutes.EVENT_STREAM_CONFIG}`, 15 | { params } 16 | ); 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /app/frontend/src/services/fileService.js: -------------------------------------------------------------------------------- 1 | import { appAxios } from '~/services/interceptors'; 2 | import { ApiRoutes } from '~/utils/constants'; 3 | 4 | export default { 5 | async deleteFile(fileId) { 6 | return appAxios().delete(`${ApiRoutes.FILES}/${fileId}`); 7 | }, 8 | async deleteFiles(fileIds) { 9 | return appAxios().delete(`${ApiRoutes.FILES}/`, { data: { fileIds } }); 10 | }, 11 | async getFile(fileId, options = {}) { 12 | return appAxios().get(`${ApiRoutes.FILES}/${fileId}`, options); 13 | }, 14 | async uploadFile(file, config = {}) { 15 | return appAxios().post(`${ApiRoutes.FILES}`, file, config); 16 | }, 17 | async cloneFile(fileId) { 18 | return appAxios().get(`${ApiRoutes.FILES}/${fileId}/clone`); 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /app/frontend/src/services/index.js: -------------------------------------------------------------------------------- 1 | export { default as adminService } from './adminService'; 2 | export { default as apiKeyService } from './apiKeyService'; 3 | export { default as formService } from './formService'; 4 | export { default as rbacService } from './rbacService'; 5 | export { default as roleService } from './roleService'; 6 | export { default as userService } from './userService'; 7 | export { default as fileService } from './fileService'; 8 | export { default as utilsService } from './utilsService'; 9 | export { default as encryptionKeyService } from './encryptionKeyService'; 10 | export { default as eventStreamConfigService } from './eventStreamConfigService'; 11 | -------------------------------------------------------------------------------- /app/frontend/src/services/roleService.js: -------------------------------------------------------------------------------- 1 | import { appAxios } from '~/services/interceptors'; 2 | import { ApiRoutes } from '~/utils/constants'; 3 | 4 | export default { 5 | // 6 | // Role Management calls 7 | // 8 | 9 | /** 10 | * @function list 11 | * List roles in the system 12 | * @returns {Promise} An axios response 13 | */ 14 | list() { 15 | return appAxios().get(`${ApiRoutes.ROLES}`); 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /app/frontend/src/services/utilsService.js: -------------------------------------------------------------------------------- 1 | import { appAxios } from '~/services/interceptors'; 2 | import { ApiRoutes } from '~/utils/constants'; 3 | 4 | export default { 5 | // 6 | // Util calls 7 | // 8 | 9 | /** 10 | * @function draftDocGen 11 | * Upload a template and submission data to generate PDF from CDOGS API 12 | * @param {Object} body The request body containing the template and submission data 13 | * @returns {Promise} An axios response 14 | */ 15 | draftDocGen(body) { 16 | return appAxios().post(`${ApiRoutes.UTILS}/template/render`, body, { 17 | responseType: 'arraybuffer', // Needed for binaries unless you want pain 18 | timeout: 30000, // Override default timeout as this call could take a while 19 | }); 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /app/frontend/src/store/app.js: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | 3 | export const useAppStore = defineStore('app', { 4 | state: () => ({ 5 | config: null, 6 | }), 7 | getters: {}, 8 | actions: {}, 9 | }); 10 | -------------------------------------------------------------------------------- /app/frontend/src/store/index.js: -------------------------------------------------------------------------------- 1 | import { createPinia } from 'pinia'; 2 | 3 | export default createPinia(); 4 | -------------------------------------------------------------------------------- /app/frontend/src/views/Admin.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 23 | -------------------------------------------------------------------------------- /app/frontend/src/views/File.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /app/frontend/src/views/Form.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /app/frontend/src/views/NotFound.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 22 | -------------------------------------------------------------------------------- /app/frontend/src/views/User.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /app/frontend/src/views/admin/Form.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 29 | -------------------------------------------------------------------------------- /app/frontend/src/views/admin/Root.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 22 | -------------------------------------------------------------------------------- /app/frontend/src/views/admin/User.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 29 | -------------------------------------------------------------------------------- /app/frontend/src/views/form/Emails.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /app/frontend/src/views/form/Export.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /app/frontend/src/views/form/Manage.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 33 | -------------------------------------------------------------------------------- /app/frontend/src/views/form/Submissions.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /app/frontend/src/views/form/Submit.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 15 | -------------------------------------------------------------------------------- /app/frontend/src/views/form/Teams.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /app/frontend/src/views/form/View.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /app/frontend/src/views/user/Forms.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /app/frontend/src/views/user/History.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /app/frontend/src/views/user/Root.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 33 | -------------------------------------------------------------------------------- /app/frontend/src/views/user/SubmissionDraftEdit.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 18 | -------------------------------------------------------------------------------- /app/frontend/src/views/user/SubmissionDuplicate.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 27 | -------------------------------------------------------------------------------- /app/frontend/src/views/user/SubmissionView.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 15 | -------------------------------------------------------------------------------- /app/frontend/src/views/user/Submissions.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/components/admin/Dashboard.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import { expect } from 'vitest'; 3 | 4 | import Dashboard from '~/components/admin/Dashboard.vue'; 5 | 6 | describe('Dashboard.vue', () => { 7 | const URL = 'http://somewhere.com'; 8 | 9 | it('renders', async () => { 10 | const wrapper = mount(Dashboard, { 11 | props: { 12 | url: URL, 13 | }, 14 | global: { 15 | plugins: [], 16 | }, 17 | }); 18 | 19 | expect(wrapper.html()).toContain(URL); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/components/base/BaseImagePopout.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import { describe, expect, it } from 'vitest'; 3 | 4 | import BaseImagePopout from '~/components/base/BaseImagePopout.vue'; 5 | 6 | describe('BaseImagePopout.vue', () => { 7 | it('renders', async () => { 8 | const wrapper = mount(BaseImagePopout, { 9 | props: { 10 | src: 'test', 11 | }, 12 | global: { 13 | stubs: ['v-dialog', 'v-hover'], 14 | }, 15 | }); 16 | expect(wrapper.html()).toMatch('v-hover'); 17 | expect(wrapper.html()).toMatch('v-dialog'); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/components/base/BaseNotificationContainer.spec.js: -------------------------------------------------------------------------------- 1 | import { createTestingPinia } from '@pinia/testing'; 2 | import { mount } from '@vue/test-utils'; 3 | import { describe, expect, it } from 'vitest'; 4 | 5 | import BaseNotificationContainer from '~/components/base/BaseNotificationContainer.vue'; 6 | 7 | describe('BaseNotificationContainer.vue', () => { 8 | it('renders', async () => { 9 | const wrapper = mount(BaseNotificationContainer, { 10 | global: { 11 | stubs: ['BaseNotificationBar'], 12 | plugins: [createTestingPinia()], 13 | }, 14 | }); 15 | 16 | expect(wrapper.html()).toMatch('notification-container'); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/components/base/BasePrintButton.spec.js: -------------------------------------------------------------------------------- 1 | // @vitest-environment happy-dom 2 | // happy-dom is required to access window.location 3 | 4 | import { mount } from '@vue/test-utils'; 5 | import { beforeEach, expect, vi } from 'vitest'; 6 | 7 | import BasePrintButton from '~/components/base/BasePrintButton.vue'; 8 | 9 | describe('BasePrintButton.vue', () => { 10 | const printSpy = vi.spyOn(window, 'print'); 11 | beforeEach(() => { 12 | printSpy.mockReset(); 13 | printSpy.mockImplementation(() => {}); 14 | }); 15 | 16 | afterAll(() => { 17 | printSpy.mockRestore(); 18 | }); 19 | 20 | it('renders nothing if authenticated, user', async () => { 21 | const wrapper = mount(BasePrintButton); 22 | 23 | wrapper.vm.printSubmission(); 24 | 25 | expect(wrapper.html()).toContain('print'); 26 | expect(printSpy).toHaveBeenCalledTimes(1); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/composables/form.spec.js: -------------------------------------------------------------------------------- 1 | // @vitest-environment happy-dom 2 | // happy-dom is required to access window.URL 3 | import * as formComposables from '~/composables/form'; 4 | 5 | describe('form.js', () => { 6 | describe('exportFormSchema', () => { 7 | it('should export some schema and return a snek', () => { 8 | const { snek } = formComposables.exportFormSchema( 9 | 'this is a form name', 10 | { 11 | id: '1', 12 | projectId: '123', 13 | }, 14 | null 15 | ); 16 | 17 | // it should transform the form name 18 | expect(snek).toEqual( 19 | 'this is a form name' 20 | .replace(/\s+/g, '_') 21 | .replace(/[^-_0-9a-z]/gi, '') 22 | .toLowerCase() 23 | ); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/composables/printOptions.spec.js: -------------------------------------------------------------------------------- 1 | // @vitest-environment happy-dom 2 | // happy-dom is required to access window.URL 3 | import { vi } from 'vitest'; 4 | import * as printOptionsComposables from '~/composables/printOptions'; 5 | 6 | describe('printOptions.js', () => { 7 | it('createDownload should createObjectURL, click the url then revokeObjectURL', () => { 8 | let createObjectURLSpy = vi.spyOn(window.URL, 'createObjectURL'); 9 | createObjectURLSpy.mockImplementation((data) => data); 10 | let revokeObjectURLSpy = vi.spyOn(window.URL, 'revokeObjectURL'); 11 | revokeObjectURLSpy.mockImplementation((data) => data); 12 | printOptionsComposables.createDownload(); 13 | expect(createObjectURLSpy).toHaveBeenCalledTimes(1); 14 | expect(revokeObjectURLSpy).toHaveBeenCalledTimes(1); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/fixtures/permissions.json: -------------------------------------------------------------------------------- 1 | [ 2 | "design_create", 3 | "design_delete", 4 | "design_read", 5 | "design_update", 6 | "document_template_create", 7 | "document_template_delete", 8 | "document_template_read", 9 | "email_template_read", 10 | "email_template_update", 11 | "form_api_create", 12 | "form_api_delete", 13 | "form_api_read", 14 | "form_api_update", 15 | "form_delete", 16 | "form_read", 17 | "form_update", 18 | "submission_create", 19 | "submission_delete", 20 | "submission_read", 21 | "submission_review", 22 | "submission_update", 23 | "team_read", 24 | "team_update" 25 | ] -------------------------------------------------------------------------------- /app/frontend/tests/unit/services/roleService.spec.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import MockAdapter from 'axios-mock-adapter'; 3 | import { beforeEach, describe, expect, it, vi } from 'vitest'; 4 | 5 | import roleService from '~/services/roleService'; 6 | import { ApiRoutes } from '~/utils/constants'; 7 | 8 | const mockInstance = axios.create(); 9 | const mockAxios = new MockAdapter(mockInstance); 10 | 11 | vi.mock('~/services/interceptors', () => { 12 | return { 13 | appAxios: () => mockInstance, 14 | }; 15 | }); 16 | 17 | describe('Role Service', () => { 18 | const endpoint = `${ApiRoutes.ROLES}`; 19 | 20 | beforeEach(() => { 21 | mockAxios.reset(); 22 | }); 23 | 24 | it('calls get on roles/ endpoint', async () => { 25 | mockAxios.onGet(endpoint).reply(200); 26 | 27 | const result = await roleService.list(); 28 | expect(result).toBeTruthy(); 29 | expect(mockAxios.history.get).toHaveLength(1); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/setup.js: -------------------------------------------------------------------------------- 1 | import 'vitest-canvas-mock'; 2 | 3 | class ResizeObserverStub { 4 | observe() {} 5 | unobserve() {} 6 | disconnect() {} 7 | } 8 | 9 | window.ResizeObserver = window.ResizeObserver || ResizeObserverStub; 10 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/views/Form.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import { describe, expect, it } from 'vitest'; 3 | 4 | import Form from '~/views/Form.vue'; 5 | 6 | describe('Form.vue', () => { 7 | it('renders', () => { 8 | const wrapper = mount(Form, { 9 | global: { 10 | stubs: { 11 | RouterView: true, 12 | }, 13 | }, 14 | }); 15 | 16 | expect(wrapper.html()).toMatch('router-view'); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/views/NotFound.spec.js: -------------------------------------------------------------------------------- 1 | import { createTestingPinia } from '@pinia/testing'; 2 | import { mount } from '@vue/test-utils'; 3 | import { setActivePinia } from 'pinia'; 4 | import { describe, expect, it } from 'vitest'; 5 | 6 | import NotFound from '~/views/NotFound.vue'; 7 | 8 | describe('NotFound.vue', () => { 9 | const pinia = createTestingPinia(); 10 | setActivePinia(pinia); 11 | 12 | it('renders', () => { 13 | const wrapper = mount(NotFound, { 14 | global: { 15 | stubs: { 16 | RouterLink: true, 17 | }, 18 | plugins: [pinia], 19 | }, 20 | }); 21 | 22 | expect(wrapper.text()).toMatch('trans.notFound.pageNotFound'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/views/User.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import { describe, expect, it } from 'vitest'; 3 | 4 | import User from '~/views/User.vue'; 5 | 6 | describe('User.vue', () => { 7 | it('renders', () => { 8 | const wrapper = mount(User, { 9 | global: { 10 | stubs: { 11 | RouterView: true, 12 | }, 13 | }, 14 | }); 15 | 16 | expect(wrapper.html()).toMatch('router-view'); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/views/admin/Form.spec.js: -------------------------------------------------------------------------------- 1 | import { createTestingPinia } from '@pinia/testing'; 2 | import { mount } from '@vue/test-utils'; 3 | import { setActivePinia } from 'pinia'; 4 | import { describe, expect, it } from 'vitest'; 5 | 6 | import Form from '~/views/admin/Form.vue'; 7 | 8 | describe('Form.vue', () => { 9 | const pinia = createTestingPinia(); 10 | setActivePinia(pinia); 11 | 12 | it('renders', () => { 13 | const wrapper = mount(Form, { 14 | props: { 15 | f: 'f', 16 | }, 17 | global: { 18 | stubs: { 19 | AdministerForm: true, 20 | }, 21 | plugins: [pinia], 22 | }, 23 | }); 24 | 25 | expect(wrapper.text()).toMatch('trans.admin.form.administerForm'); 26 | expect(wrapper.html()).toMatch('administer-form'); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/views/admin/Root.spec.js: -------------------------------------------------------------------------------- 1 | import { createTestingPinia } from '@pinia/testing'; 2 | import { mount } from '@vue/test-utils'; 3 | import { setActivePinia } from 'pinia'; 4 | import { describe, expect, it } from 'vitest'; 5 | 6 | import Root from '~/views/admin/Root.vue'; 7 | 8 | describe('Root.vue', () => { 9 | const pinia = createTestingPinia(); 10 | setActivePinia(pinia); 11 | 12 | it('renders', () => { 13 | const wrapper = mount(Root, { 14 | props: { 15 | f: 'f', 16 | }, 17 | global: { 18 | stubs: { 19 | AdminPage: true, 20 | }, 21 | plugins: [pinia], 22 | }, 23 | }); 24 | 25 | expect(wrapper.text()).toMatch('trans.admin.root.admin'); 26 | expect(wrapper.html()).toMatch('admin-page'); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/views/admin/User.spec.js: -------------------------------------------------------------------------------- 1 | import { createTestingPinia } from '@pinia/testing'; 2 | import { mount } from '@vue/test-utils'; 3 | import { setActivePinia } from 'pinia'; 4 | import { describe, expect, it } from 'vitest'; 5 | 6 | import User from '~/views/admin/User.vue'; 7 | 8 | describe('User.vue', () => { 9 | const pinia = createTestingPinia(); 10 | setActivePinia(pinia); 11 | 12 | it('renders', () => { 13 | const wrapper = mount(User, { 14 | props: { 15 | u: 'u', 16 | }, 17 | global: { 18 | stubs: { 19 | AdministerUser: true, 20 | }, 21 | plugins: [pinia], 22 | }, 23 | }); 24 | 25 | expect(wrapper.text()).toMatch('trans.admin.user.administerUser'); 26 | expect(wrapper.html()).toMatch('administer-user'); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/views/form/Export.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import { describe, expect, it } from 'vitest'; 3 | 4 | import Export from '~/views/form/Export.vue'; 5 | 6 | describe('Export.vue', () => { 7 | it('renders', () => { 8 | const wrapper = mount(Export, { 9 | props: { 10 | f: 'f', 11 | }, 12 | global: { 13 | stubs: { 14 | BaseSecure: { 15 | name: 'BaseSecure', 16 | template: '
', 17 | }, 18 | ExportSubmissions: true, 19 | }, 20 | }, 21 | }); 22 | 23 | expect(wrapper.html()).toMatch('base-secure'); 24 | expect(wrapper.html()).toMatch('export-submissions'); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/views/form/Manage.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import { describe, expect, it } from 'vitest'; 3 | 4 | import Manage from '~/views/form/Manage.vue'; 5 | 6 | describe('Manage.vue', () => { 7 | it('renders', () => { 8 | const wrapper = mount(Manage, { 9 | props: { 10 | f: 'f', 11 | }, 12 | global: { 13 | stubs: { 14 | BaseSecure: { 15 | name: 'BaseSecure', 16 | template: '
', 17 | }, 18 | ManageLayout: true, 19 | }, 20 | }, 21 | }); 22 | 23 | expect(wrapper.html()).toMatch('base-secure'); 24 | expect(wrapper.html()).toMatch('manage-layout'); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/views/form/Submissions.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import { describe, expect, it } from 'vitest'; 3 | 4 | import Submissions from '~/views/form/Submissions.vue'; 5 | 6 | describe('Submissions.vue', () => { 7 | it('renders', () => { 8 | const wrapper = mount(Submissions, { 9 | props: { 10 | f: 'f', 11 | }, 12 | global: { 13 | stubs: { 14 | BaseSecure: { 15 | name: 'BaseSecure', 16 | template: '
', 17 | }, 18 | SubmissionsTable: true, 19 | }, 20 | }, 21 | }); 22 | 23 | expect(wrapper.html()).toMatch('base-secure'); 24 | expect(wrapper.html()).toMatch('submissions-table'); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/views/form/Submit.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import { describe, expect, it } from 'vitest'; 3 | 4 | import Submit from '~/views/form/Submit.vue'; 5 | 6 | describe('Submit.vue', () => { 7 | it('renders', () => { 8 | const wrapper = mount(Submit, { 9 | props: { 10 | f: 'f', 11 | }, 12 | global: { 13 | stubs: { 14 | FormViewer: true, 15 | }, 16 | }, 17 | }); 18 | 19 | expect(wrapper.html()).toMatch('form-viewer'); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/views/form/Success.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import { setActivePinia, createPinia } from 'pinia'; 3 | import { describe, expect, it } from 'vitest'; 4 | import { nextTick } from 'vue'; 5 | 6 | import Success from '~/views/form/Success.vue'; 7 | 8 | describe('Success.vue', () => { 9 | const pinia = createPinia(); 10 | setActivePinia(pinia); 11 | it('renders', async () => { 12 | const wrapper = mount(Success, { 13 | props: { 14 | s: 's', 15 | f: 'f', 16 | }, 17 | global: { 18 | plugins: [pinia], 19 | stubs: { 20 | FormViewer: true, 21 | RequestReceipt: true, 22 | }, 23 | }, 24 | }); 25 | 26 | await nextTick(); 27 | 28 | expect(wrapper.html()).toMatch('form-viewer'); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/views/form/Teams.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import { describe, expect, it } from 'vitest'; 3 | 4 | import Teams from '~/views/form/Teams.vue'; 5 | 6 | describe('Teams.vue', () => { 7 | it('renders', () => { 8 | const wrapper = mount(Teams, { 9 | props: { 10 | f: 'f', 11 | }, 12 | global: { 13 | stubs: { 14 | BaseSecure: { 15 | name: 'BaseSecure', 16 | template: '
', 17 | }, 18 | TeamManagement: true, 19 | }, 20 | }, 21 | }); 22 | 23 | expect(wrapper.html()).toMatch('base-secure'); 24 | expect(wrapper.html()).toMatch('team-management'); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/views/form/View.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import { describe, expect, it } from 'vitest'; 3 | 4 | import View from '~/views/form/View.vue'; 5 | 6 | describe('View.vue', () => { 7 | it('renders', () => { 8 | const wrapper = mount(View, { 9 | props: { 10 | s: 's', 11 | }, 12 | global: { 13 | stubs: { 14 | BaseSecure: { 15 | name: 'BaseSecure', 16 | template: '
', 17 | }, 18 | FormSubmission: true, 19 | }, 20 | }, 21 | }); 22 | 23 | expect(wrapper.html()).toMatch('base-secure'); 24 | expect(wrapper.html()).toMatch('form-submission'); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/views/user/Forms.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import { describe, expect, it } from 'vitest'; 3 | 4 | import Forms from '~/views/user/Forms.vue'; 5 | 6 | describe('Forms.vue', () => { 7 | it('renders', () => { 8 | const wrapper = mount(Forms, { 9 | global: { 10 | stubs: { 11 | FormsTable: true, 12 | }, 13 | }, 14 | }); 15 | 16 | expect(wrapper.html()).toMatch('forms-table'); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/views/user/History.spec.js: -------------------------------------------------------------------------------- 1 | import { createTestingPinia } from '@pinia/testing'; 2 | import { mount } from '@vue/test-utils'; 3 | import { setActivePinia } from 'pinia'; 4 | import { describe, expect, it } from 'vitest'; 5 | 6 | import History from '~/views/user/History.vue'; 7 | 8 | describe('History.vue', () => { 9 | const pinia = createTestingPinia(); 10 | setActivePinia(pinia); 11 | 12 | it('renders', () => { 13 | const wrapper = mount(History, { 14 | global: { 15 | stubs: { 16 | BaseSecure: { 17 | name: 'BaseSecure', 18 | template: '
', 19 | }, 20 | }, 21 | plugins: [pinia], 22 | }, 23 | }); 24 | 25 | expect(wrapper.text()).toMatch('trans.history.submissnHistory'); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/views/user/SubmissionDraftEdit.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import { describe, expect, it } from 'vitest'; 3 | 4 | import SubmissionDraftEdit from '~/views/user/SubmissionDraftEdit.vue'; 5 | 6 | describe('SubmissionDraftEdit.vue', () => { 7 | it('renders', () => { 8 | const wrapper = mount(SubmissionDraftEdit, { 9 | props: { 10 | s: 's', 11 | }, 12 | global: { 13 | stubs: { 14 | UserSubmission: true, 15 | }, 16 | }, 17 | }); 18 | 19 | expect(wrapper.html()).toMatch('user-submission'); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/views/user/SubmissionDuplicate.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import { describe, expect, it } from 'vitest'; 3 | 4 | import SubmissionDuplicate from '~/views/user/SubmissionDuplicate.vue'; 5 | 6 | describe('SubmissionDuplicate.vue', () => { 7 | it('renders', () => { 8 | const wrapper = mount(SubmissionDuplicate, { 9 | props: { 10 | s: 's', 11 | f: 'f', 12 | }, 13 | global: { 14 | stubs: { 15 | UserDuplicateSubmission: true, 16 | }, 17 | }, 18 | }); 19 | 20 | expect(wrapper.html()).toMatch('user-duplicate-submission'); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/views/user/SubmissionView.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import { describe, expect, it } from 'vitest'; 3 | 4 | import SubmissionView from '~/views/user/SubmissionView.vue'; 5 | 6 | describe('SubmissionView.vue', () => { 7 | it('renders', () => { 8 | const wrapper = mount(SubmissionView, { 9 | props: { 10 | s: 's', 11 | }, 12 | global: { 13 | stubs: { 14 | FormSubmission: true, 15 | }, 16 | }, 17 | }); 18 | 19 | expect(wrapper.html()).toMatch('form-submission'); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /app/frontend/tests/unit/views/user/Submissions.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import { describe, expect, it } from 'vitest'; 3 | 4 | import Submissions from '~/views/user/Submissions.vue'; 5 | 6 | describe('Submissions.vue', () => { 7 | it('renders', () => { 8 | const wrapper = mount(Submissions, { 9 | props: { 10 | f: 'f', 11 | }, 12 | global: { 13 | stubs: { 14 | BaseSecure: { 15 | name: 'BaseSecure', 16 | template: '
', 17 | }, 18 | MySubmissionsTable: true, 19 | }, 20 | }, 21 | }); 22 | 23 | expect(wrapper.html()).toMatch('base-secure'); 24 | expect(wrapper.html()).toMatch('my-submissions-table'); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /app/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | clearMocks: true, 3 | collectCoverage: true, 4 | collectCoverageFrom: ['src/**/*.js', '!src/db/migrations/*.js', '!src/db/seeds/*.js', '!src/forms/common/models/(tables|views)/*.js', '!frontend/**/*.*'], 5 | moduleFileExtensions: ['js', 'json'], 6 | moduleNameMapper: { 7 | '^~/(.*)$': '/src/$1', 8 | }, 9 | testMatch: ['**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'], 10 | testPathIgnorePatterns: ['frontend'], 11 | testEnvironmentOptions: { 12 | url: 'http://localhost/', 13 | }, 14 | transformIgnorePatterns: ['/node_modules/(?!@json2csv|@streamparser)'], 15 | }; 16 | -------------------------------------------------------------------------------- /app/lcov-fix.js: -------------------------------------------------------------------------------- 1 | // Jest 25.x onwards emits coverage reports on a different source path 2 | // https://stackoverflow.com/q/60323177 3 | const fs = require('fs'); 4 | const file = './coverage/lcov.info'; 5 | 6 | fs.readFile(file, 'utf8', (err, data) => { 7 | if (err) { 8 | return console.error(err); // eslint-disable-line no-console 9 | } 10 | const result = data.replace(/src/g, `${process.cwd()}/src`); 11 | 12 | fs.writeFile(file, result, 'utf8', (err) => { 13 | if (err) return console.error(err); // eslint-disable-line no-console 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /app/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignore": [ 3 | "frontend/node_modules", 4 | "frontend/src", 5 | "frontend/tests", 6 | "node_modules/**/node_modules", 7 | "tests" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /app/src/db/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.js] 2 | indent_size = unset 3 | -------------------------------------------------------------------------------- /app/src/db/migrations/20201007112921_005-submissions-data-vw.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function(knex) { 3 | return Promise.resolve() 4 | .then(() => knex.schema.raw(`create or replace view submissions_data_vw as 5 | select s."confirmationId", s."formName", s.version, s."createdAt", 6 | u."fullName", u.username, u.email, 7 | fs.submission -> 'data' AS "submission", s.deleted, s.draft, 8 | s."submissionId", s."formId", s."formVersionId", u.id as "userId", u."keycloakId", u."firstName", u."lastName" 9 | from submissions_vw s 10 | inner join form_submission fs on s."submissionId" = fs.id 11 | inner join form_submission_user fsu on s."submissionId" = fsu."formSubmissionId" and fsu.permission = 'submission_create' 12 | inner join "user" u on fsu."userId" = u.id 13 | order by s."createdAt", s."formName", s.version`)); 14 | }; 15 | 16 | exports.down = function(knex) { 17 | return Promise.resolve() 18 | .then(() => knex.schema.raw('DROP VIEW IF EXISTS submissions_data_vw')); 19 | }; 20 | -------------------------------------------------------------------------------- /app/src/db/migrations/20201029141347_008-form-settings-notifications.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function (knex) { 3 | return Promise.resolve() 4 | .then(() => knex.schema.alterTable('form', table => { 5 | table.boolean('showSubmissionConfirmation').notNullable().defaultTo(true).comment('Show (when true) confirmation/receipt information to the submitter on success'); 6 | table.specificType('submissionReceivedEmails', 'text ARRAY').comment('Array of email addresses to deliver notifications after a submission is received.'); 7 | })); 8 | }; 9 | 10 | exports.down = function (knex) { 11 | return Promise.resolve() 12 | .then(() => knex.schema.alterTable('form', table => { 13 | table.dropColumn('showSubmissionConfirmation'); 14 | table.dropColumn('submissionReceivedEmails'); 15 | })); 16 | }; 17 | -------------------------------------------------------------------------------- /app/src/db/migrations/20201105120909_009-file-upload.js: -------------------------------------------------------------------------------- 1 | const stamps = require('../stamps'); 2 | 3 | exports.up = function (knex) { 4 | return Promise.resolve().then(() => 5 | knex.schema.createTable('file_storage', (table) => { 6 | table.uuid('id').primary(); 7 | table.string('originalName', 1024).notNullable(); 8 | table.string('mimeType').notNullable(); 9 | table.integer('size').notNullable(); 10 | table.enu('storage', ['uploads', 'localStorage', 'objectStorage']).notNullable().defaultTo('uploads'); 11 | table.string('path', 1024).notNullable(); 12 | table.uuid('formSubmissionId').references('id').inTable('form_submission').nullable().index(); 13 | stamps(knex, table); 14 | }) 15 | ); 16 | }; 17 | 18 | exports.down = function (knex) { 19 | return Promise.resolve().then(() => knex.schema.dropTableIfExists('file_storage')); 20 | }; 21 | -------------------------------------------------------------------------------- /app/src/db/migrations/20210528134214_016-submission-submitters-vw copy.js: -------------------------------------------------------------------------------- 1 | exports.up = function(knex) { 2 | return Promise.resolve() 3 | .then(() => knex.schema.raw(`create or replace view submissions_submitters_vw as 4 | SELECT 5 | fsu.*, 6 | fv."formId", 7 | fv."version", 8 | sub."id", 9 | sub."confirmationId", 10 | sub."createdAt", 11 | sub."draft", 12 | sub."deleted" 13 | FROM 14 | form_submission_users_vw fsu 15 | INNER JOIN form_submission sub ON 16 | sub."id" = fsu."formSubmissionId" 17 | INNER JOIN form_version fv ON 18 | fv."id" = sub."formVersionId"`)); 19 | }; 20 | 21 | exports.down = function(knex) { 22 | return Promise.resolve() 23 | .then(() => knex.schema.raw('DROP VIEW IF EXISTS submissions_submitters_vw')); 24 | }; 25 | -------------------------------------------------------------------------------- /app/src/db/migrations/20210816000000_020-user-form-preferences.js: -------------------------------------------------------------------------------- 1 | const stamps = require('../stamps'); 2 | 3 | exports.up = function (knex) { 4 | return Promise.resolve() 5 | .then(() => knex.schema.createTable('user_form_preferences', table => { 6 | table.primary(['userId', 'formId']); 7 | table.uuid('userId').references('id').inTable('user').notNullable(); 8 | table.uuid('formId').references('id').inTable('form').notNullable(); 9 | table.jsonb('preferences'); 10 | stamps(knex, table); 11 | })); 12 | }; 13 | 14 | exports.down = function (knex) { 15 | return Promise.resolve() 16 | .then(() => knex.schema.dropTableIfExists('user_form_preferences')); 17 | }; 18 | -------------------------------------------------------------------------------- /app/src/db/migrations/20210928000000_022-add-bceid-idps.js: -------------------------------------------------------------------------------- 1 | const CREATED_BY = 'migration-022'; 2 | 3 | exports.up = function(knex) { 4 | return Promise.resolve() 5 | .then(() => { 6 | return knex('identity_provider').insert([ 7 | { 8 | createdBy: CREATED_BY, 9 | code: 'bceid-basic', 10 | display: 'Basic BCeID', 11 | active: true, 12 | idp: 'bceid-basic' 13 | }, 14 | { 15 | createdBy: CREATED_BY, 16 | code: 'bceid-business', 17 | display: 'Business BCeID', 18 | active: true, 19 | idp: 'bceid-business' 20 | } 21 | ]); 22 | }); 23 | }; 24 | 25 | exports.down = function(knex) { 26 | return Promise.resolve() 27 | .then(() => { 28 | return knex('identity_provider').where('createdBy', CREATED_BY).del(); 29 | }); 30 | }; 31 | -------------------------------------------------------------------------------- /app/src/db/migrations/20211006000000_023-add-user-idps.js: -------------------------------------------------------------------------------- 1 | exports.up = function (knex) { 2 | return Promise.resolve() 3 | // Add optional idpCode foreign key column 4 | .then(() => knex.schema.alterTable('user', table => { 5 | table.string('idpCode').references('code').inTable('identity_provider').comment('The associated identity provider'); 6 | })) 7 | // Set idpCode to 'idir' 8 | .then(() => knex('user').update({ idpCode: 'idir' })); 9 | }; 10 | 11 | exports.down = function (knex) { 12 | return Promise.resolve() 13 | .then(() => knex.schema.alterTable('user', table => { 14 | table.dropColumn('idpCode'); 15 | })); 16 | }; 17 | -------------------------------------------------------------------------------- /app/src/db/migrations/20221205215308_025-add-idp-guids.js: -------------------------------------------------------------------------------- 1 | exports.up = function(knex) { 2 | return Promise.resolve() 3 | .then(() => knex.schema.alterTable('user', table => { 4 | table.string('idpUserId').comment('The unique identifier provided by the identity provider'); 5 | })); 6 | }; 7 | 8 | exports.down = function(knex) { 9 | return Promise.resolve() 10 | .then(() => knex.schema.alterTable('user', table => { 11 | table.dropColumn('idpUserId'); 12 | })); 13 | }; 14 | -------------------------------------------------------------------------------- /app/src/db/migrations/20230110063945_027-form_components_proactive_help.js: -------------------------------------------------------------------------------- 1 | const stamps = require('../stamps'); 2 | 3 | exports.up = function (knex) { 4 | return Promise.resolve().then(() => 5 | knex.schema.createTable('form_components_proactive_help', (table) => { 6 | table.uuid('id').primary(); 7 | table.string('componentName').notNullable(); 8 | table.string('externalLink'); 9 | table.binary('image'); 10 | table.string('imageType'); 11 | table.string('componentImageName'); 12 | table.string('groupName').notNullable(); 13 | table.text('description'); 14 | table.boolean('isLinkEnabled').defaultTo(false); 15 | table.boolean('publishStatus').defaultTo(false); 16 | stamps(knex, table); 17 | }) 18 | ); 19 | }; 20 | 21 | exports.down = function (knex) { 22 | return Promise.resolve().then(() => knex.schema.dropTableIfExists('form_components_proactive_help')); 23 | }; 24 | -------------------------------------------------------------------------------- /app/src/db/migrations/20230110063945_028-alter-form-table-to-add-reminder-schedule-copy-col.js: -------------------------------------------------------------------------------- 1 | exports.up = function (knex) { 2 | return Promise.resolve().then(() => 3 | knex.schema.alterTable('form', (table) => { 4 | table.jsonb('schedule').comment('Form level Schedule settings.'); 5 | table.boolean('reminder_enabled').comment('Form level reminder settings.'); 6 | table.boolean('enableCopyExistingSubmission').notNullable().defaultTo(false).comment('Form level feature settings.'); 7 | }) 8 | ); 9 | }; 10 | 11 | exports.down = function (knex) { 12 | return Promise.resolve().then(() => 13 | knex.schema.alterTable('form', (table) => { 14 | table.dropColumn('schedule'); 15 | table.dropColumn('reminder_enabled'); 16 | table.dropColumn('enableCopyExistingSubmission'); 17 | }) 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /app/src/db/migrations/20230412141347_029-form-settings-uploadfile.js: -------------------------------------------------------------------------------- 1 | exports.up = function (knex) { 2 | return Promise.resolve().then(() => 3 | knex.schema.alterTable('form', (table) => { 4 | table.boolean('allowSubmitterToUploadFile').notNullable().defaultTo(false).comment('This parameter allow submitter to load data from json file'); 5 | }) 6 | ); 7 | }; 8 | 9 | exports.down = function (knex) { 10 | return Promise.resolve().then(() => 11 | knex.schema.alterTable('form', (table) => { 12 | table.dropColumn('allowSubmitterToUploadFile'); 13 | }) 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /app/src/db/migrations/20230705190020_change_file_storage_constrains.js: -------------------------------------------------------------------------------- 1 | exports.up = function (knex) { 2 | return Promise.resolve() 3 | .then(() => knex.schema.raw(`ALTER TABLE file_storage DROP CONSTRAINT file_storage_storage_check`)) 4 | .then(() => 5 | knex.schema.raw(`ALTER TABLE file_storage ADD CONSTRAINT 6 | file_storage_storage_check CHECK 7 | ((storage = ANY (ARRAY['uploads'::text, 'localStorage'::text, 'objectStorage'::text, 'exports'::text])))`) 8 | ); 9 | }; 10 | 11 | exports.down = function (knex) { 12 | return Promise.resolve() 13 | .then(() => knex.schema.raw(`ALTER TABLE file_storage DROP CONSTRAINT file_storage_storage_check`)) 14 | .then(() => 15 | knex.schema.raw(`ALTER TABLE file_storage ADD CONSTRAINT 16 | file_storage_storage_check CHECK 17 | ((storage = ANY (ARRAY['uploads'::text, 'localStorage'::text, 'objectStorage'::text, 'exports'::text])))`) 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /app/src/db/migrations/20231123172822_send-team-notification-email.js: -------------------------------------------------------------------------------- 1 | exports.up = function (knex) { 2 | return Promise.resolve() 3 | .then(() => knex.schema.alterTable('form', table => { 4 | table.boolean('sendSubmissionReceivedEmail').defaultTo(true).comment('Keeps track of if forms should send a notification email'); 5 | })); 6 | }; 7 | 8 | exports.down = function (knex) { 9 | return Promise.resolve() 10 | .then(() => knex.schema.alterTable('form', table => { 11 | table.dropColumn('sendSubmissionReceivedEmail'); 12 | })); 13 | }; 14 | -------------------------------------------------------------------------------- /app/src/db/migrations/20240115201832_files-api-access.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param { import("knex").Knex } knex 3 | * @returns { Promise } 4 | */ 5 | exports.up = function(knex) { 6 | return Promise.resolve() 7 | .then(() => knex.schema.alterTable('form_api_key', table => { 8 | table.boolean('filesApiAccess').defaultTo(false).comment('Keeps track of whether files can be accessed using the API key'); 9 | })); 10 | }; 11 | 12 | /** 13 | * @param { import("knex").Knex } knex 14 | * @returns { Promise } 15 | */ 16 | exports.down = function(knex) { 17 | return Promise.resolve() 18 | .then(() => knex.schema.alterTable('form_api_key', table => { 19 | table.dropColumn('filesApiAccess'); 20 | })); 21 | }; 22 | -------------------------------------------------------------------------------- /app/src/db/migrations/20240312164557_remove-confirmationid-constraint.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param { import("knex").Knex } knex 3 | * @returns { Promise } 4 | */ 5 | exports.up = function (knex) { 6 | return Promise.resolve().then(() => 7 | knex.schema.alterTable('form_submission', (table) => { 8 | table.dropUnique('confirmationId'); 9 | }) 10 | ); 11 | }; 12 | 13 | /** 14 | * @param { import("knex").Knex } knex 15 | * @returns { Promise } 16 | */ 17 | exports.down = function (knex) { 18 | return Promise.resolve().then(() => 19 | knex.schema.alterTable('form_submission', (table) => { 20 | table.unique('confirmationId'); 21 | }) 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /app/src/db/migrations/20240321170550_wide-form-layout.js: -------------------------------------------------------------------------------- 1 | exports.up = function (knex) { 2 | return Promise.resolve() 3 | .then(() => knex.schema.alterTable('form', table => { 4 | table.boolean('wideFormLayout').defaultTo(false).comment('Tracks if the form should be displayed in a wide layout'); 5 | })); 6 | }; 7 | 8 | exports.down = function (knex) { 9 | return Promise.resolve() 10 | .then(() => knex.schema.alterTable('form', table => { 11 | table.dropColumn('wideFormLayout'); 12 | })); 13 | }; 14 | 15 | -------------------------------------------------------------------------------- /app/src/db/migrations/20240916104713_048-email-recipients.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param { import("knex").Knex } knex 3 | * @returns { Promise } 4 | */ 5 | exports.up = function(knex) { 6 | return Promise.resolve() 7 | .then(() => knex.schema.alterTable('form_submission_status', table => { 8 | table.specificType('emailRecipients', 'text ARRAY').comment('Array of email addresses (form submitters) to deliver notifications when the status is set to revising.'); 9 | })); 10 | }; 11 | 12 | /** 13 | * @param { import("knex").Knex } knex 14 | * @returns { Promise } 15 | */ 16 | exports.down = function(knex) { 17 | return Promise.resolve() 18 | .then(() => knex.schema.alterTable('form_submission_status', table => { 19 | table.dropColumn('emailRecipients'); 20 | })); 21 | }; 22 | -------------------------------------------------------------------------------- /app/src/db/migrations/20241016164117__050-form-metadata.js: -------------------------------------------------------------------------------- 1 | const stamps = require('../stamps'); 2 | 3 | /** 4 | * @param { import("knex").Knex } knex 5 | * @returns { Promise } 6 | */ 7 | 8 | exports.up = function (knex) { 9 | return Promise.resolve().then(() => 10 | knex.schema.createTable('form_metadata', (table) => { 11 | table.uuid('id').primary(); 12 | table.uuid('formId').references('id').inTable('form').notNullable().index(); 13 | table.jsonb('metadata'); 14 | stamps(knex, table); 15 | }) 16 | ); 17 | }; 18 | 19 | /** 20 | * @param { import("knex").Knex } knex 21 | * @returns { Promise } 22 | */ 23 | exports.down = function (knex) { 24 | return Promise.resolve().then(() => knex.schema.dropTableIfExists('form_metadata')); 25 | }; 26 | -------------------------------------------------------------------------------- /app/src/db/migrations/20250319062555_alter-form-table-to-enable_team_member_draft_share.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param { import("knex").Knex } knex 3 | * @returns { Promise } 4 | */ 5 | exports.up = function (knex) { 6 | return Promise.resolve().then(() => 7 | knex.schema.alterTable('form', (table) => { 8 | table.boolean('enableTeamMemberDraftShare').notNullable().defaultTo(false); 9 | }) 10 | ); 11 | }; 12 | 13 | exports.down = function (knex) { 14 | return Promise.resolve().then(() => 15 | knex.schema.alterTable('form', (table) => { 16 | table.dropColumn('enableTeamMemberDraftShare'); 17 | }) 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /app/src/db/migrations/20250502163949_enable-bcsc-idp.js: -------------------------------------------------------------------------------- 1 | const UPDATED_BY = 'enable-bcsc-idp-migration'; 2 | 3 | /** 4 | * @param { import("knex").Knex } knex 5 | * @returns { Promise } 6 | */ 7 | exports.up = function (knex) { 8 | return knex('identity_provider').where({ code: 'bcservicescard' }).update({ 9 | active: true, 10 | updatedBy: UPDATED_BY, 11 | updatedAt: knex.fn.now(), 12 | }); 13 | }; 14 | 15 | /** 16 | * @param { import("knex").Knex } knex 17 | * @returns { Promise } 18 | */ 19 | exports.down = function (knex) { 20 | return knex('identity_provider') 21 | .where({ code: 'bcservicescard' }) 22 | .update({ 23 | active: false, 24 | updatedBy: `${UPDATED_BY}-rollback`, 25 | updatedAt: knex.fn.now(), 26 | }); 27 | }; 28 | -------------------------------------------------------------------------------- /app/src/db/stamps.js: -------------------------------------------------------------------------------- 1 | module.exports = (knex, table) => { 2 | table.string('createdBy').defaultTo('public'); 3 | table.timestamp('createdAt', { useTz: true }).defaultTo(knex.fn.now()); 4 | table.string('updatedBy'); 5 | table.timestamp('updatedAt', { useTz: true }).defaultTo(knex.fn.now()); 6 | }; 7 | -------------------------------------------------------------------------------- /app/src/docs/.editorconfig: -------------------------------------------------------------------------------- 1 | [*postman_collection.json] 2 | indent_style = unset 3 | indent_size = unset 4 | -------------------------------------------------------------------------------- /app/src/docs/docs.js: -------------------------------------------------------------------------------- 1 | const config = require('config'); 2 | 3 | const docs = { 4 | getDocHTML: (version) => ` 5 | 6 | 7 | Common Hosted Form Service - Documentation ${version} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | `, 23 | }; 24 | 25 | module.exports = docs; 26 | -------------------------------------------------------------------------------- /app/src/forms/admin/fileService.js: -------------------------------------------------------------------------------- 1 | const storageService = require('../file/storage/storageService'); 2 | 3 | const fileService = { 4 | create: async (imageData) => { 5 | return await storageService.uploadImage(imageData); 6 | }, 7 | 8 | signedUrl: async (param) => { 9 | return await storageService.readSignedUrl(param.imageName); 10 | }, 11 | }; 12 | 13 | module.exports = fileService; 14 | -------------------------------------------------------------------------------- /app/src/forms/admin/index.js: -------------------------------------------------------------------------------- 1 | const routes = require('./routes'); 2 | const setupMount = require('../common/utils').setupMount; 3 | 4 | module.exports.mount = (app) => { 5 | return setupMount('admin', app, routes); 6 | }; 7 | -------------------------------------------------------------------------------- /app/src/forms/bcgeoaddress/controller.js: -------------------------------------------------------------------------------- 1 | const service = require('./service'); 2 | 3 | module.exports = { 4 | searchBCGeoAddress: async (req, res, next) => { 5 | try { 6 | const { query } = req; 7 | const response = await service.searchBCGeoAddress(query); 8 | res.status(200).json(response); 9 | } catch (error) { 10 | next(error); 11 | } 12 | }, 13 | advanceSearchBCGeoAddress: async (req, res, next) => { 14 | try { 15 | const { query } = req; 16 | const response = await service.advanceSearchBCGeoAddress(query); 17 | res.status(200).json(response); 18 | } catch (error) { 19 | next(error); 20 | } 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /app/src/forms/bcgeoaddress/index.js: -------------------------------------------------------------------------------- 1 | const routes = require('./routes'); 2 | const setupMount = require('../common/utils').setupMount; 3 | 4 | module.exports.mount = (app) => { 5 | return setupMount('bcgeoaddress', app, routes); 6 | }; 7 | -------------------------------------------------------------------------------- /app/src/forms/bcgeoaddress/routes.js: -------------------------------------------------------------------------------- 1 | const cors = require('cors'); 2 | const routes = require('express').Router(); 3 | const { Development } = require('../common/constants'); 4 | 5 | const controller = require('./controller'); 6 | 7 | // need to allow cors for OPTIONS call (localhost only) 8 | // formio component will call OPTIONS pre-flight 9 | routes.options('/advance/address', cors({ origin: Development.LOCALHOST_ORIGIN })); 10 | 11 | routes.get('/address', async (req, res, next) => { 12 | await controller.searchBCGeoAddress(req, res, next); 13 | }); 14 | 15 | routes.get('/advance/address', cors({ origin: Development.LOCALHOST_ORIGIN }), async (req, res, next) => { 16 | await controller.advanceSearchBCGeoAddress(req, res, next); 17 | }); 18 | 19 | module.exports = routes; 20 | -------------------------------------------------------------------------------- /app/src/forms/bcgeoaddress/service.js: -------------------------------------------------------------------------------- 1 | const geoAddressService = require('../../components/geoAddressService'); 2 | 3 | const service = { 4 | searchBCGeoAddress: async (query) => { 5 | let addresses = { features: [] }; 6 | 7 | let searchAddresses = await geoAddressService.addressQuerySearch(query); 8 | 9 | if (searchAddresses?.features && Array.isArray(searchAddresses.features)) { 10 | searchAddresses.features.forEach((element) => { 11 | if (element?.properties?.fullAddress) { 12 | addresses.features.push({ geometry: { coordinates: element.geometry.coordinates }, properties: { fullAddress: element.properties.fullAddress } }); 13 | } 14 | }); 15 | } 16 | return addresses; 17 | }, 18 | 19 | advanceSearchBCGeoAddress: async (query) => { 20 | return await geoAddressService.addressQuerySearch(query); 21 | }, 22 | }; 23 | 24 | module.exports = service; 25 | -------------------------------------------------------------------------------- /app/src/forms/common/middleware/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ...require('./errorHandler'), 3 | ...require('./rateLimiter'), 4 | }; 5 | -------------------------------------------------------------------------------- /app/src/forms/common/models/jsonSchema.js: -------------------------------------------------------------------------------- 1 | module.exports.stamps = { 2 | createdBy: { type: ['string', 'null'] }, 3 | createdAt: { type: ['string', 'null'] }, 4 | updatedBy: { type: ['string', 'null'] }, 5 | updatedAt: { type: ['string', 'null'] }, 6 | }; 7 | -------------------------------------------------------------------------------- /app/src/forms/common/models/tables/essAllowlist.js: -------------------------------------------------------------------------------- 1 | const { Model } = require('objection'); 2 | const { Timestamps } = require('../mixins'); 3 | const stamps = require('../jsonSchema').stamps; 4 | 5 | class EssAllowlist extends Timestamps(Model) { 6 | static get tableName() { 7 | return 'ess_allowlist'; 8 | } 9 | 10 | static get modifiers() { 11 | return { 12 | findByAccountName(query, accountName) { 13 | if (accountName !== undefined) { 14 | query.where('accountName', accountName); 15 | } 16 | }, 17 | }; 18 | } 19 | static get jsonSchema() { 20 | return { 21 | type: 'object', 22 | required: ['accountName'], 23 | properties: { 24 | accountName: { type: 'string' }, 25 | notes: { type: 'object' }, 26 | ...stamps, 27 | }, 28 | additionalProperties: false, 29 | }; 30 | } 31 | } 32 | 33 | module.exports = EssAllowlist; 34 | -------------------------------------------------------------------------------- /app/src/forms/common/models/tables/externalAPIStatusCode.js: -------------------------------------------------------------------------------- 1 | const { Model } = require('objection'); 2 | const { Timestamps } = require('../mixins'); 3 | const stamps = require('../jsonSchema').stamps; 4 | 5 | class ExternalAPIStatusCode extends Timestamps(Model) { 6 | static get tableName() { 7 | return 'external_api_status_code'; 8 | } 9 | 10 | static get jsonSchema() { 11 | return { 12 | type: 'object', 13 | required: ['code'], 14 | properties: { 15 | code: { type: 'string' }, 16 | display: { type: 'string' }, 17 | ...stamps, 18 | }, 19 | additionalProperties: false, 20 | }; 21 | } 22 | } 23 | 24 | module.exports = ExternalAPIStatusCode; 25 | -------------------------------------------------------------------------------- /app/src/forms/common/models/tables/formIdentityProvider.js: -------------------------------------------------------------------------------- 1 | const { Model } = require('objection'); 2 | const { Timestamps } = require('../mixins'); 3 | const { Regex } = require('../../constants'); 4 | const stamps = require('../jsonSchema').stamps; 5 | 6 | class FormIdentityProvider extends Timestamps(Model) { 7 | static get tableName() { 8 | return 'form_identity_provider'; 9 | } 10 | 11 | static get jsonSchema() { 12 | return { 13 | type: 'object', 14 | required: ['formId'], 15 | properties: { 16 | id: { type: 'string', pattern: Regex.UUID }, 17 | formId: { type: 'string', pattern: Regex.UUID }, 18 | code: { type: 'string', maxLength: 255 }, 19 | ...stamps, 20 | }, 21 | additionalProperties: false, 22 | }; 23 | } 24 | } 25 | 26 | module.exports = FormIdentityProvider; 27 | -------------------------------------------------------------------------------- /app/src/forms/common/models/tables/label.js: -------------------------------------------------------------------------------- 1 | const { Model } = require('objection'); 2 | const { Timestamps } = require('../mixins'); 3 | const { Regex } = require('../../constants'); 4 | const stamps = require('../jsonSchema').stamps; 5 | 6 | class Label extends Timestamps(Model) { 7 | static get tableName() { 8 | return 'label'; 9 | } 10 | 11 | static get jsonSchema() { 12 | return { 13 | type: 'object', 14 | required: ['userId'], 15 | properties: { 16 | id: { type: 'string', pattern: Regex.UUID }, 17 | userId: { type: 'string', pattern: Regex.UUID }, 18 | labelText: { type: 'string', minLength: 1, maxLength: 25 }, 19 | ...stamps, 20 | }, 21 | additionalProperties: false, 22 | }; 23 | } 24 | } 25 | 26 | module.exports = Label; 27 | -------------------------------------------------------------------------------- /app/src/forms/common/models/tables/statusCode.js: -------------------------------------------------------------------------------- 1 | const { Model } = require('objection'); 2 | const { Timestamps } = require('../mixins'); 3 | const { Regex } = require('../../constants'); 4 | const stamps = require('../jsonSchema').stamps; 5 | 6 | class StatusCode extends Timestamps(Model) { 7 | static get tableName() { 8 | return 'status_code'; 9 | } 10 | 11 | static get jsonSchema() { 12 | return { 13 | type: 'object', 14 | required: ['code'], 15 | properties: { 16 | code: { type: 'string' }, 17 | display: { type: 'string' }, 18 | nextCodes: { type: ['array', 'null'], items: { type: 'string', pattern: Regex.EMAIL } }, 19 | ...stamps, 20 | }, 21 | additionalProperties: false, 22 | }; 23 | } 24 | } 25 | 26 | module.exports = StatusCode; 27 | -------------------------------------------------------------------------------- /app/src/forms/common/models/tables/userFormPreferences.js: -------------------------------------------------------------------------------- 1 | const { Model } = require('objection'); 2 | const { Timestamps } = require('../mixins'); 3 | const { Regex } = require('../../constants'); 4 | const stamps = require('../jsonSchema').stamps; 5 | 6 | class UserFormPreferences extends Timestamps(Model) { 7 | static get tableName() { 8 | return 'user_form_preferences'; 9 | } 10 | 11 | // This is a composite key - order of attributes is important 12 | static get idColumn() { 13 | return ['userId', 'formId']; 14 | } 15 | 16 | static get jsonSchema() { 17 | return { 18 | type: 'object', 19 | required: ['formId', 'userId'], 20 | properties: { 21 | userId: { type: 'string', pattern: Regex.UUID }, 22 | formId: { type: 'string', pattern: Regex.UUID }, 23 | preferences: { type: 'object' }, 24 | ...stamps, 25 | }, 26 | additionalProperties: false, 27 | }; 28 | } 29 | } 30 | 31 | module.exports = UserFormPreferences; 32 | -------------------------------------------------------------------------------- /app/src/forms/common/models/utils.js: -------------------------------------------------------------------------------- 1 | const toArray = (values) => { 2 | if (values) { 3 | return Array.isArray(values) ? values.filter((p) => p && p.trim().length > 0) : [values].filter((p) => p && p.trim().length > 0); 4 | } 5 | return []; 6 | }; 7 | const inArrayClause = (column, values) => { 8 | return values.map((p) => `'${p}' = ANY("${column}")`).join(' or '); 9 | }; 10 | 11 | const inArrayFilter = (column, values) => { 12 | const clause = inArrayClause(column, values); 13 | return `(array_length("${column}", 1) > 0 and (${clause}))`; 14 | }; 15 | 16 | const tableNames = (models) => { 17 | return Object.values(models).map((model) => model.tableName); 18 | }; 19 | 20 | module.exports = { 21 | toArray, 22 | inArrayClause, 23 | inArrayFilter, 24 | tableNames, 25 | }; 26 | -------------------------------------------------------------------------------- /app/src/forms/common/models/views/publicFormAccess.js: -------------------------------------------------------------------------------- 1 | const { Model } = require('objection'); 2 | 3 | class PublicFormAccess extends Model { 4 | static get tableName() { 5 | return 'public_form_access_vw'; 6 | } 7 | 8 | static get modifiers() { 9 | return { 10 | filterActive(query, value) { 11 | if (value !== undefined) { 12 | query.where('active', value); 13 | } 14 | }, 15 | filterFormId(query, value) { 16 | if (value) { 17 | query.where('formId', value); 18 | } 19 | }, 20 | orderDefault(builder) { 21 | builder.orderByRaw('lower("formName")'); 22 | }, 23 | }; 24 | } 25 | } 26 | 27 | module.exports = PublicFormAccess; 28 | -------------------------------------------------------------------------------- /app/src/forms/email/assets/bodies/file-download-ready.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 |
4 |
5 |

6 | {{ messageLinkText }}
7 | Download file 8 |

9 |
10 |
13 | -------------------------------------------------------------------------------- /app/src/forms/email/assets/bodies/reminder-form-not-fill.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 26 | 27 |
7 |
14 |

15 | {{ messageLinkText }} 16 |

17 | 18 |

19 | 20 | View this Form 21 | 22 |

23 | 24 |
25 |
28 | -------------------------------------------------------------------------------- /app/src/forms/email/assets/bodies/reminder-form-open.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 22 | 23 |
4 |
10 |

11 | {{ messageLinkText }} 12 |

13 | 14 |

15 | 16 | View this Form 17 | 18 |

19 | 20 |
21 |
24 | -------------------------------------------------------------------------------- /app/src/forms/email/assets/bodies/reminder-form-will-close.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 24 | 25 |
7 |
14 |

15 | {{ messageLinkText }} 16 |

17 |

18 | 19 | View this Form 20 | 21 |

22 |
23 |
26 | -------------------------------------------------------------------------------- /app/src/forms/email/assets/bodies/submission-assigned.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 29 | 30 |
7 |
14 |

15 | {{ messageLinkText }} 16 |

17 |

18 | 19 | View this draft 20 | 21 |

22 |

23 | 24 | View your {{ form.name }} submissions 25 | 26 |

27 |
28 |
31 | -------------------------------------------------------------------------------- /app/src/forms/email/assets/bodies/submission-unassigned.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 20 |
7 |
14 |

15 | {{ messageLinkText }} 16 |

17 |
18 |
21 | -------------------------------------------------------------------------------- /app/src/forms/file/index.js: -------------------------------------------------------------------------------- 1 | const config = require('config'); 2 | const setupMount = require('../common/utils').setupMount; 3 | const routes = require('./routes'); 4 | 5 | const _PATH = config.get('files.uploads.path') || 'files'; 6 | 7 | const fileUpload = require('./middleware/upload').fileUpload; 8 | 9 | fileUpload.init({ 10 | dir: config.has('files.uploads.dir') ? config.get('files.uploads.dir') : undefined, 11 | fieldName: config.has('files.uploads.fileKey') ? config.get('files.uploads.fileKey') : undefined, 12 | maxFileCount: config.has('files.uploads.fileCount') ? config.get('files.uploads.fileCount') : undefined, 13 | maxFileSize: config.has('files.uploads.fileMaxSize') ? config.get('files.uploads.fileMaxSize') : undefined, 14 | }); 15 | 16 | module.exports.mount = (app) => { 17 | return setupMount(_PATH, app, routes); 18 | }; 19 | -------------------------------------------------------------------------------- /app/src/forms/form/encryptionKey/controller.js: -------------------------------------------------------------------------------- 1 | const service = require('./service'); 2 | 3 | module.exports = { 4 | listEncryptionAlgorithms: async (req, res, next) => { 5 | try { 6 | const response = await service.listEncryptionAlgorithms(); 7 | res.status(200).json(response); 8 | } catch (error) { 9 | next(error); 10 | } 11 | }, 12 | readEncryptionKey: async (req, res, next) => { 13 | try { 14 | const response = await service.readEncryptionKey(req.params.formId, req.params.formEncryptionKeyId); 15 | res.status(200).json(response); 16 | } catch (error) { 17 | next(error); 18 | } 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /app/src/forms/form/encryptionKey/routes.js: -------------------------------------------------------------------------------- 1 | const routes = require('express').Router(); 2 | const { currentUser, hasFormPermissions } = require('../../auth/middleware/userAccess'); 3 | const validateParameter = require('../../common/middleware/validateParameter'); 4 | const P = require('../../common/constants').Permissions; 5 | 6 | const controller = require('./controller'); 7 | 8 | routes.use(currentUser); 9 | 10 | routes.param('formId', validateParameter.validateFormId); 11 | routes.param('formEncryptionKeyId', validateParameter.validateFormEncryptionKeyId); 12 | 13 | routes.get('/encryptionKey/algorithms', async (req, res, next) => { 14 | await controller.listEncryptionAlgorithms(req, res, next); 15 | }); 16 | 17 | routes.get('/:formId/encryptionKey/:formEncryptionKeyId', hasFormPermissions([P.FORM_READ, P.FORM_UPDATE]), async (req, res, next) => { 18 | await controller.readEncryptionKey(req, res, next); 19 | }); 20 | 21 | module.exports = routes; 22 | -------------------------------------------------------------------------------- /app/src/forms/form/eventStreamConfig/controller.js: -------------------------------------------------------------------------------- 1 | const service = require('./service'); 2 | 3 | module.exports = { 4 | readEventStreamConfig: async (req, res, next) => { 5 | try { 6 | const response = await service.readEventStreamConfig(req.params.formId); 7 | res.status(200).json(response); 8 | } catch (error) { 9 | next(error); 10 | } 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /app/src/forms/form/eventStreamConfig/routes.js: -------------------------------------------------------------------------------- 1 | const routes = require('express').Router(); 2 | const { currentUser, hasFormPermissions } = require('../../auth/middleware/userAccess'); 3 | const validateParameter = require('../../common/middleware/validateParameter'); 4 | const P = require('../../common/constants').Permissions; 5 | 6 | const controller = require('./controller'); 7 | 8 | routes.use(currentUser); 9 | 10 | routes.param('formId', validateParameter.validateFormId); 11 | 12 | routes.get('/:formId/eventStreamConfig', hasFormPermissions([P.FORM_READ, P.FORM_UPDATE]), async (req, res, next) => { 13 | await controller.readEventStreamConfig(req, res, next); 14 | }); 15 | 16 | module.exports = routes; 17 | -------------------------------------------------------------------------------- /app/src/forms/form/externalApi/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcgov/common-hosted-form-service/ee209435541c048da1bc081f42fd5008ae415c06/app/src/forms/form/externalApi/index.js -------------------------------------------------------------------------------- /app/src/forms/form/index.js: -------------------------------------------------------------------------------- 1 | const routes = require('./routes'); 2 | const setupMount = require('../common/utils').setupMount; 3 | 4 | const encryptionKeyRoutes = require('./encryptionKey/routes'); 5 | const eventStreamConfigRoutes = require('./eventStreamConfig/routes'); 6 | const externalApiRoutes = require('./externalApi/routes'); 7 | 8 | module.exports.mount = (app) => { 9 | const p = setupMount('forms', app, [routes, encryptionKeyRoutes, eventStreamConfigRoutes, externalApiRoutes]); 10 | return p; 11 | }; 12 | -------------------------------------------------------------------------------- /app/src/forms/permission/controller.js: -------------------------------------------------------------------------------- 1 | const service = require('./service'); 2 | 3 | module.exports = { 4 | list: async (req, res, next) => { 5 | try { 6 | const response = await service.list(); 7 | res.status(200).json(response); 8 | } catch (error) { 9 | next(error); 10 | } 11 | }, 12 | 13 | read: async (req, res, next) => { 14 | try { 15 | const response = await service.read(req.params.code); 16 | res.status(200).json(response); 17 | } catch (error) { 18 | next(error); 19 | } 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /app/src/forms/permission/index.js: -------------------------------------------------------------------------------- 1 | const routes = require('./routes'); 2 | const setupMount = require('../common/utils').setupMount; 3 | 4 | module.exports.mount = (app) => { 5 | return setupMount('permissions', app, routes); 6 | }; 7 | -------------------------------------------------------------------------------- /app/src/forms/permission/routes.js: -------------------------------------------------------------------------------- 1 | const routes = require('express').Router(); 2 | 3 | const jwtService = require('../../components/jwtService'); 4 | const currentUser = require('../auth/middleware/userAccess').currentUser; 5 | const validateParameter = require('../common/middleware/validateParameter'); 6 | const controller = require('./controller'); 7 | 8 | routes.use(jwtService.protect('admin')); 9 | routes.use(currentUser); 10 | 11 | routes.param('code', validateParameter.validatePermissionCode); 12 | 13 | routes.get('/', async (req, res, next) => { 14 | await controller.list(req, res, next); 15 | }); 16 | 17 | routes.get('/:code', async (req, res, next) => { 18 | await controller.read(req, res, next); 19 | }); 20 | 21 | module.exports = routes; 22 | -------------------------------------------------------------------------------- /app/src/forms/proxy/error.js: -------------------------------------------------------------------------------- 1 | class ProxyServiceError extends Error { 2 | constructor(message) { 3 | super(message); 4 | this.name = 'ProxyServiceError'; 5 | } 6 | } 7 | 8 | module.exports = ProxyServiceError; 9 | -------------------------------------------------------------------------------- /app/src/forms/proxy/index.js: -------------------------------------------------------------------------------- 1 | const routes = require('./routes'); 2 | const setupMount = require('../common/utils').setupMount; 3 | 4 | module.exports.mount = (app) => { 5 | return setupMount('proxy', app, routes); 6 | }; 7 | -------------------------------------------------------------------------------- /app/src/forms/proxy/routes.js: -------------------------------------------------------------------------------- 1 | const cors = require('cors'); 2 | const routes = require('express').Router(); 3 | const { Development } = require('../common/constants'); 4 | 5 | const { currentUser } = require('../auth/middleware/userAccess'); 6 | const controller = require('./controller'); 7 | 8 | // need to allow cors for OPTIONS call (localhost only) 9 | // formio component will call OPTIONS pre-flight 10 | routes.options('/external', cors({ origin: Development.LOCALHOST_ORIGIN })); 11 | 12 | // called with encrypted headers, no current user!!! 13 | routes.get('/external', cors({ origin: Development.LOCALHOST_ORIGIN }), async (req, res, next) => { 14 | await controller.callExternalApi(req, res, next); 15 | }); 16 | 17 | routes.post('/headers', currentUser, async (req, res, next) => { 18 | await controller.generateProxyHeaders(req, res, next); 19 | }); 20 | 21 | module.exports = routes; 22 | -------------------------------------------------------------------------------- /app/src/forms/public/controller.js: -------------------------------------------------------------------------------- 1 | const service = require('./service'); 2 | module.exports = { 3 | sendReminderToSubmitter: async (req, res, next) => { 4 | try { 5 | const response = await service.sendReminderToSubmitter(); 6 | res.status(200).json(response); 7 | } catch (error) { 8 | next(error); 9 | } 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /app/src/forms/public/index.js: -------------------------------------------------------------------------------- 1 | const routes = require('./routes'); 2 | const setupMount = require('../common/utils').setupMount; 3 | 4 | module.exports.mount = (app) => { 5 | return setupMount('public', app, routes); 6 | }; 7 | -------------------------------------------------------------------------------- /app/src/forms/public/routes.js: -------------------------------------------------------------------------------- 1 | const routes = require('express').Router(); 2 | 3 | const apiAccess = require('./middleware/apiAccess'); 4 | const controller = require('./controller'); 5 | 6 | routes.get('/reminder', apiAccess.checkApiKey, async (req, res, next) => { 7 | await controller.sendReminderToSubmitter(req, res, next); 8 | }); 9 | 10 | module.exports = routes; 11 | -------------------------------------------------------------------------------- /app/src/forms/public/service.js: -------------------------------------------------------------------------------- 1 | const reminderService = require('../email/reminderService'); 2 | const service = { 3 | sendReminderToSubmitter: async () => { 4 | return await reminderService._init(); 5 | }, 6 | }; 7 | 8 | module.exports = service; 9 | -------------------------------------------------------------------------------- /app/src/forms/rbac/index.js: -------------------------------------------------------------------------------- 1 | const routes = require('./routes'); 2 | const setupMount = require('../common/utils').setupMount; 3 | 4 | module.exports.mount = (app) => { 5 | return setupMount('rbac', app, routes); 6 | }; 7 | -------------------------------------------------------------------------------- /app/src/forms/role/controller.js: -------------------------------------------------------------------------------- 1 | const service = require('./service'); 2 | 3 | module.exports = { 4 | list: async (req, res, next) => { 5 | try { 6 | const response = await service.list(); 7 | res.status(200).json(response); 8 | } catch (error) { 9 | next(error); 10 | } 11 | }, 12 | 13 | read: async (req, res, next) => { 14 | try { 15 | const response = await service.read(req.params.code); 16 | res.status(200).json(response); 17 | } catch (error) { 18 | next(error); 19 | } 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /app/src/forms/role/index.js: -------------------------------------------------------------------------------- 1 | const routes = require('./routes'); 2 | const setupMount = require('../common/utils').setupMount; 3 | 4 | module.exports.mount = (app) => { 5 | return setupMount('roles', app, routes); 6 | }; 7 | -------------------------------------------------------------------------------- /app/src/forms/role/routes.js: -------------------------------------------------------------------------------- 1 | const routes = require('express').Router(); 2 | 3 | const jwtService = require('../../components/jwtService'); 4 | const currentUser = require('../auth/middleware/userAccess').currentUser; 5 | const validateParameter = require('../common/middleware/validateParameter'); 6 | const controller = require('./controller'); 7 | 8 | jwtService.protect(); 9 | routes.use(currentUser); 10 | 11 | routes.param('code', validateParameter.validateRoleCode); 12 | 13 | routes.get('/', async (req, res, next) => { 14 | await controller.list(req, res, next); 15 | }); 16 | 17 | routes.get('/:code', async (req, res, next) => { 18 | await controller.read(req, res, next); 19 | }); 20 | 21 | module.exports = routes; 22 | -------------------------------------------------------------------------------- /app/src/forms/role/service.js: -------------------------------------------------------------------------------- 1 | const { Role } = require('../common/models'); 2 | 3 | const service = { 4 | list: async () => { 5 | return Role.query().allowGraph('[permissions]').withGraphFetched('permissions(orderDefault)').modify('orderDefault'); 6 | }, 7 | 8 | read: async (code) => { 9 | return Role.query().findOne('code', code).allowGraph('[permissions]').withGraphFetched('permissions(orderDefault)').throwIfNotFound(); 10 | }, 11 | }; 12 | 13 | module.exports = service; 14 | -------------------------------------------------------------------------------- /app/src/forms/submission/index.js: -------------------------------------------------------------------------------- 1 | const routes = require('./routes'); 2 | const setupMount = require('../common/utils').setupMount; 3 | 4 | module.exports.mount = (app) => { 5 | return setupMount('submissions', app, routes); 6 | }; 7 | -------------------------------------------------------------------------------- /app/src/forms/user/index.js: -------------------------------------------------------------------------------- 1 | const routes = require('./routes'); 2 | const setupMount = require('../common/utils').setupMount; 3 | 4 | module.exports.mount = (app) => { 5 | return setupMount('users', app, routes); 6 | }; 7 | -------------------------------------------------------------------------------- /app/src/forms/utils/index.js: -------------------------------------------------------------------------------- 1 | const routes = require('./routes'); 2 | const setupMount = require('../common/utils').setupMount; 3 | 4 | module.exports.mount = (app) => { 5 | return setupMount('utils', app, routes); 6 | }; 7 | -------------------------------------------------------------------------------- /app/src/forms/utils/routes.js: -------------------------------------------------------------------------------- 1 | const routes = require('express').Router(); 2 | 3 | const { currentUser } = require('../auth/middleware/userAccess'); 4 | const controller = require('../submission/controller'); 5 | 6 | routes.use(currentUser); 7 | 8 | routes.post('/template/render', async (req, res, next) => { 9 | await controller.draftTemplateUploadAndRender(req, res, next); 10 | }); 11 | 12 | module.exports = routes; 13 | -------------------------------------------------------------------------------- /app/tests/fixtures/submission/kitchen_sink_submission_data_export_datagrid_fields_selection.json: -------------------------------------------------------------------------------- 1 | [ 2 | "form.confirmationId", 3 | "form.formName", 4 | "form.version", 5 | "form.createdAt", 6 | "form.fullName", 7 | "form.username", 8 | "form.email", 9 | "fishermansName", 10 | "email", 11 | "forWhichBcLakeRegionAreYouCompletingTheseQuestions", 12 | "didYouFishAnyBcLakesThisYear", 13 | "oneRowPerLake.0.lakeName", 14 | "oneRowPerLake.0.closestTown", 15 | "oneRowPerLake.0.numberOfDays", 16 | "oneRowPerLake.0.dataGrid.0.fishType", 17 | "oneRowPerLake.0.dataGrid.1.fishType", 18 | "oneRowPerLake.0.dataGrid.0.numberCaught", 19 | "oneRowPerLake.0.dataGrid.1.numberCaught", 20 | "oneRowPerLake.0.dataGrid.0.numberKept", 21 | "oneRowPerLake.0.dataGrid.1.numberKept" 22 | ] 23 | -------------------------------------------------------------------------------- /app/tests/unit/components/errorToProblem.spec.js: -------------------------------------------------------------------------------- 1 | const errorToProblem = require('../../../src/components/errorToProblem'); 2 | 3 | const SERVICE = 'TESTSERVICE'; 4 | 5 | describe('errorToProblem', () => { 6 | it('should throw a 404', () => { 7 | const error = { 8 | response: { 9 | data: { detail: 'detail' }, 10 | status: 404, 11 | }, 12 | }; 13 | 14 | expect(() => errorToProblem(SERVICE, error)).toThrow('404'); 15 | }); 16 | 17 | it('should throw a 422', () => { 18 | const error = { 19 | response: { 20 | data: { detail: 'detail' }, 21 | status: 422, 22 | }, 23 | }; 24 | 25 | expect(() => errorToProblem(SERVICE, error)).toThrow('422'); 26 | }); 27 | 28 | it('should throw a 502', () => { 29 | const error = { 30 | message: 'msg', 31 | }; 32 | 33 | expect(() => errorToProblem(SERVICE, error)).toThrow('502'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /clamav/.dockerignore: -------------------------------------------------------------------------------- 1 | charts/ -------------------------------------------------------------------------------- /clamav/charts/clamav/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | clamav.yaml 25 | -------------------------------------------------------------------------------- /clamav/charts/clamav/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: clamav 3 | description: Chart for deploying a Clam AV on kubernetes 4 | type: application 5 | version: 1.0.0 6 | appVersion: 1.0.5 7 | keywords: 8 | - clamav 9 | maintainers: 10 | - name: BC Gov 11 | url: https://github.com/bcgov/clamav 12 | kubeVersion: ">= 1.18.0" 13 | -------------------------------------------------------------------------------- /clamav/charts/clamav/templates/nsp.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: {{ include "clamav.fullname" . }}-network-policy 5 | namespace: {{ .Release.Namespace }} 6 | labels: 7 | {{- include "clamav.labels" . | nindent 4 }} 8 | spec: 9 | podSelector: 10 | matchLabels: 11 | {{- include "clamav.selectorLabels" . | nindent 6 }} 12 | ingress: 13 | - from: 14 | - podSelector: 15 | matchLabels: 16 | role: {{ .Values.nsp.role }} 17 | namespaceSelector: 18 | matchLabels: 19 | environment: {{ .Values.nsp.environment }} 20 | name: {{ .Values.nsp.name }} 21 | policyTypes: 22 | - Ingress -------------------------------------------------------------------------------- /clamav/charts/clamav/templates/pdb.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.pdb.create }} 2 | apiVersion: policy/v1 3 | kind: PodDisruptionBudget 4 | metadata: 5 | name: {{ include "clamav.fullname" . }} 6 | namespace: {{ .Release.Namespace }} 7 | labels: {{- include "clamav.labels" . | nindent 4 }} 8 | spec: 9 | {{- if .Values.pdb.minAvailable }} 10 | minAvailable: {{ .Values.pdb.minAvailable }} 11 | {{- end }} 12 | {{- if .Values.pdb.maxUnavailable }} 13 | maxUnavailable: {{ .Values.pdb.maxUnavailable }} 14 | {{- end }} 15 | selector: 16 | matchLabels: {{- include "clamav.selectorLabels" . | nindent 6 }} 17 | {{- end }} 18 | -------------------------------------------------------------------------------- /clamav/charts/clamav/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "clamav.fullname" . }} 5 | namespace: {{ .Release.Namespace }} 6 | labels: 7 | {{- include "clamav.labels" . | nindent 4 }} 8 | spec: 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: 3310 12 | protocol: TCP 13 | name: clamav 14 | selector: 15 | {{- include "clamav.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /clamav/charts/clamav/values-dev.yaml: -------------------------------------------------------------------------------- 1 | nsp: 2 | environment: dev 3 | -------------------------------------------------------------------------------- /clamav/charts/clamav/values-test.yaml: -------------------------------------------------------------------------------- 1 | nsp: 2 | environment: test 3 | -------------------------------------------------------------------------------- /clamav/clamdcheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | if [ "${CLAMAV_NO_CLAMD:-}" != "false" ]; then 6 | if [ "$(echo "PING" | nc localhost 3310)" != "PONG" ]; then 7 | echo "ERROR: Unable to contact server" 8 | exit 1 9 | fi 10 | 11 | echo "Clamd is up" 12 | fi 13 | 14 | exit 0 -------------------------------------------------------------------------------- /components/.gitignore: -------------------------------------------------------------------------------- 1 | # Editor directories and files 2 | .DS_Store 3 | lib 4 | dist 5 | node_modules 6 | 7 | # Editor directories and files 8 | .idea 9 | .vscode 10 | *.iml 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | *.sw? 16 | *.mp4 17 | 18 | -------------------------------------------------------------------------------- /components/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /components/gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const gulp = require('gulp'); 3 | const insert = require('gulp-insert'); 4 | const rename = require('gulp-rename'); 5 | const template = require('gulp-template'); 6 | 7 | // Compile all *.ejs files to pre-compiled templates and append *.js to the filename. 8 | gulp.task('templates', () => 9 | gulp.src('./src/**/*.ejs') 10 | .pipe(template.precompile({ 11 | evaluate: /\{%([\s\S]+?)%\}/g, 12 | interpolate: /\{\{([\s\S]+?)\}\}/g, 13 | escape: /\{\{\{([\s\S]+?)\}\}\}/g, 14 | variable: 'ctx' 15 | })) 16 | .pipe(insert.prepend('Object.defineProperty(exports, "__esModule", {\n' + 17 | ' value: true\n' + 18 | '});\n' + 19 | 'exports.default=')) 20 | .pipe(rename({ 21 | extname: '.ejs.js' 22 | })) 23 | .pipe(gulp.dest('lib')) 24 | ); 25 | -------------------------------------------------------------------------------- /components/src/components/BCAddress/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/_classes/component/Component.form'; 2 | import AddressEditProvider from './editForm/Address.edit.provider'; 3 | 4 | export default function(...extend) { 5 | return baseEditForm([ 6 | { 7 | label: 'Provider', 8 | key: 'provider', 9 | weight: 150, 10 | components: AddressEditProvider, 11 | }, 12 | ], ...extend); 13 | } 14 | -------------------------------------------------------------------------------- /components/src/components/Common/Advanced.edit.api.ts: -------------------------------------------------------------------------------- 1 | import ComponentEditApi from 'formiojs/components/_classes/component/editForm/Component.edit.api'; 2 | export default [ 3 | ...ComponentEditApi 4 | ]; -------------------------------------------------------------------------------- /components/src/components/Common/Advanced.edit.conditional.ts: -------------------------------------------------------------------------------- 1 | import ComponentEditConditional from 'formiojs/components/_classes/component/editForm/Component.edit.conditional'; 2 | export default [ 3 | ...ComponentEditConditional 4 | ]; -------------------------------------------------------------------------------- /components/src/components/Common/Advanced.edit.data.ts: -------------------------------------------------------------------------------- 1 | import ComponentEditData from 'formiojs/components/_classes/component/editForm/Component.edit.data'; 2 | import TextFieldEditData from 'formiojs/components/textfield/editForm/TextField.edit.data'; 3 | export default [...ComponentEditData, ...TextFieldEditData]; -------------------------------------------------------------------------------- /components/src/components/Common/Advanced.edit.layout.ts: -------------------------------------------------------------------------------- 1 | import ComponentEditLayout from 'formiojs/components/_classes/component/editForm/Component.edit.layout'; 2 | export default [ 3 | ...ComponentEditLayout 4 | ]; -------------------------------------------------------------------------------- /components/src/components/Common/Advanced.edit.logic.ts: -------------------------------------------------------------------------------- 1 | import ComponentEditLogic from 'formiojs/components/_classes/component/editForm/Component.edit.logic'; 2 | export default [ 3 | ...ComponentEditLogic 4 | ]; -------------------------------------------------------------------------------- /components/src/components/Common/Constants.ts: -------------------------------------------------------------------------------- 1 | export abstract class Constants { 2 | static readonly DEFAULT_HELP_LINK: string = 3 | 'https://developer.gov.bc.ca/docs/default/component/chefs-techdocs'; 4 | static readonly ADV: string = ''; 5 | } 6 | -------------------------------------------------------------------------------- /components/src/components/Common/Evaluator.ts: -------------------------------------------------------------------------------- 1 | const Evaluator = { 2 | noeval: false, 3 | protectedEval: false, // This property can be customized only by plugins 4 | }; 5 | 6 | export default Evaluator; 7 | -------------------------------------------------------------------------------- /components/src/components/Common/Simple.edit.api.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | weight: 0, 4 | type: 'textfield', 5 | input: true, 6 | key: 'key', 7 | label: 'Property Name', 8 | tooltip: 'The name of this field in the API endpoint.', 9 | validate: { 10 | pattern: '(\\w|\\w[\\w-.]*\\w)', 11 | patternMessage: 'The property name must only contain alphanumeric characters, underscores, dots and dashes and should not be ended by dash or dot.' 12 | } 13 | }, 14 | ]; 15 | -------------------------------------------------------------------------------- /components/src/components/Common/Simple.edit.data.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | export default [ 3 | { 4 | weight: 0, 5 | type: 'checkbox', 6 | label: 'Multiple Values', 7 | tooltip: 'Allows multiple values to be entered for this field.', 8 | key: 'multiple', 9 | input: true 10 | }, 11 | { 12 | type: 'textfield', 13 | label: 'Default Value', 14 | key: 'defaultValue', 15 | weight: 5, 16 | placeholder: 'Default Value', 17 | tooltip: 'The will be the value for this field, before user interaction. Having a default value will override the placeholder text.', 18 | input: true 19 | }, 20 | ]; 21 | /* eslint-enable max-len */ 22 | -------------------------------------------------------------------------------- /components/src/components/Common/Simple.edit.display.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | key: 'tableView', 4 | ignore: true 5 | }, 6 | { 7 | key: 'hidden', 8 | ignore: true 9 | }, 10 | { 11 | key: 'autofocus', 12 | ignore: true 13 | }, 14 | { 15 | key: 'tabindex', 16 | ignore: true 17 | }, 18 | { 19 | key: 'modalEdit', 20 | ignore: true 21 | }, 22 | { 23 | key: 'customClass', 24 | ignore: true 25 | }, 26 | ]; 27 | -------------------------------------------------------------------------------- /components/src/components/Common/Simple.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import UseForCopy from './UseForCopy'; 2 | /* eslint-disable quotes, max-len */ 3 | export default [ 4 | UseForCopy, 5 | { 6 | weight: 10, 7 | type: 'checkbox', 8 | label: 'Required', 9 | tooltip: 'A required field must be filled in before the form can be submitted.', 10 | key: 'validate.required', 11 | input: true 12 | }, 13 | { 14 | weight: 200, 15 | key: 'validate.customMessage', 16 | label: 'Custom Error Message', 17 | placeholder: 'Custom Error Message', 18 | type: 'textfield', 19 | tooltip: 'Error message displayed if any error occurred.', 20 | input: true 21 | } 22 | ]; 23 | /* eslint-enable quotes, max-len */ 24 | -------------------------------------------------------------------------------- /components/src/components/Common/UseForCopy.ts: -------------------------------------------------------------------------------- 1 | const UseForCopy = { 2 | weight: 10, 3 | type: 'checkbox', 4 | defaultValue: false, 5 | label: 'Allow value propagation', 6 | tooltip: 'Enabling this checkbox will allow this field to be duplicated in new submission.', 7 | key: 'validate.isUseForCopy', 8 | input: true 9 | }; 10 | 11 | export default UseForCopy; 12 | -------------------------------------------------------------------------------- /components/src/components/Common/function.ts: -------------------------------------------------------------------------------- 1 | export function reArrangeComponents(neededposition=[], components=[]) { 2 | const newPosition = []; 3 | // tslint:disable-next-line: no-unused-expression 4 | neededposition.length && neededposition.map((posKey) => { 5 | components.findIndex((comp) => { 6 | if(comp.key === posKey){ 7 | newPosition.push(comp); 8 | } 9 | }); 10 | }); 11 | 12 | return Array.from(new Set(newPosition.map(a => a.key))) 13 | .map(key => { 14 | return newPosition.find(a => a.key === key) 15 | }); 16 | } -------------------------------------------------------------------------------- /components/src/components/Map/Common/Constants.d.ts: -------------------------------------------------------------------------------- 1 | export declare abstract class Constants { 2 | static readonly DEFAULT_HELP_LINK: string; 3 | static readonly ADV: string; 4 | } 5 | -------------------------------------------------------------------------------- /components/src/components/Map/Common/Constants.js: -------------------------------------------------------------------------------- 1 | export class Constants { 2 | static DEFAULT_HELP_LINK = 'https://github.com/bcgov/common-hosted-form-service/wiki'; 3 | static ADV = ''; 4 | } 5 | -------------------------------------------------------------------------------- /components/src/components/Map/Common/marker-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcgov/common-hosted-form-service/ee209435541c048da1bc081f42fd5008ae415c06/components/src/components/Map/Common/marker-icon.png -------------------------------------------------------------------------------- /components/src/components/Map/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | const customValidation = [ 2 | { 3 | weight: 3, 4 | type: 'checkbox', 5 | label: 'Required', 6 | tooltip: 7 | 'A required field must be filled in before the form can be submitted.', 8 | key: 'validate.required', 9 | input: true, 10 | }, 11 | { 12 | weight: 200, 13 | key: 'validate.customMessage', 14 | label: 'Custom Error Message', 15 | placeholder: 'Custom Error Message', 16 | type: 'textfield', 17 | tooltip: 'Error message displayed if any error occurred.', 18 | input: true, 19 | }, 20 | ]; 21 | export default { 22 | key: 'customValidation', 23 | label: 'Validation', 24 | weight: 10, 25 | components: customValidation, 26 | }; 27 | -------------------------------------------------------------------------------- /components/src/components/OrgBook/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/_classes/component/Component.form'; 2 | 3 | import EditData from './editForm/Orgbook.edit.data'; 4 | import EditDisplay from './editForm/Orgbook.edit.display'; 5 | import EditValidation from './editForm/Orgbook.edit.validation'; 6 | 7 | export default function(...extend) { 8 | return baseEditForm([ 9 | { 10 | key: 'display', 11 | components: EditDisplay 12 | }, 13 | { 14 | key: 'data', 15 | components: EditData 16 | }, 17 | { 18 | key: 'validation', 19 | components: EditValidation 20 | } 21 | ], ...extend); 22 | } 23 | -------------------------------------------------------------------------------- /components/src/components/OrgBook/editForm/Orgbook.edit.display.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Simple.edit.display'; 2 | export default [ 3 | ...common, 4 | ]; 5 | -------------------------------------------------------------------------------- /components/src/components/OrgBook/editForm/Orgbook.edit.validation.ts: -------------------------------------------------------------------------------- 1 | export default []; 2 | -------------------------------------------------------------------------------- /components/src/components/SimpleAddressAdvanced/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/address/Address.form'; 2 | import EditValidation from './editForm/Component.edit.validation'; 3 | 4 | export default function(...extend) { 5 | return baseEditForm([ 6 | { 7 | key: 'validation', 8 | ignore: true 9 | }, 10 | { 11 | label: 'Validation', 12 | key: 'customValidation', 13 | weight: 15, 14 | components: EditValidation 15 | }, 16 | ], ...extend); 17 | } 18 | -------------------------------------------------------------------------------- /components/src/components/SimpleAddressAdvanced/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Advanced.edit.validation'; 2 | import {reArrangeComponents} from '../../Common/function'; 3 | 4 | const neededposition = [ 5 | 'validate.isUseForCopy', 6 | 'validate.required', 7 | 'unique', 8 | 'validateOn', 9 | 'errorLabel', 10 | 'validate.customMessage', 11 | 'custom-validation-js', 12 | 'json-validation-json', 13 | 'errors', 14 | ]; 15 | 16 | const newPosition = reArrangeComponents(neededposition,common); 17 | 18 | export default newPosition; -------------------------------------------------------------------------------- /components/src/components/SimpleBCAddress/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/_classes/component/Component.form'; 2 | import AddressEditProvider from './editForm/Address.edit.provider'; 3 | 4 | export default function(...extend) { 5 | return baseEditForm([ 6 | { 7 | label: 'Provider', 8 | key: 'provider', 9 | weight: 150, 10 | components: AddressEditProvider, 11 | }, 12 | ], ...extend); 13 | } 14 | -------------------------------------------------------------------------------- /components/src/components/SimpleButtonAdvanced/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/button/Button.form'; 2 | 3 | export default baseEditForm; 4 | -------------------------------------------------------------------------------- /components/src/components/SimpleButtonReset/editForm/Component.edit.display.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Simple.edit.display'; 2 | export default { 3 | key: 'display', 4 | components: [ 5 | ...common, 6 | { 7 | key: 'refreshOnChange', 8 | ignore: true 9 | }, 10 | { 11 | key: 'className', 12 | ignore: true, 13 | }, 14 | { 15 | key: 'labelPosition', 16 | ignore: true, 17 | }, 18 | { 19 | key: 'placeholder', 20 | ignore: true, 21 | }, 22 | { 23 | key: 'hideLabel', 24 | ignore: true, 25 | }, 26 | { 27 | type: 'checkbox', 28 | key: 'block', 29 | label: 'Block Button', 30 | input: true, 31 | weight: 155, 32 | tooltip: 'This control should span the full width of the bounding container.', 33 | }, 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /components/src/components/SimpleCheckbox/editForm/Component.edit.display.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Simple.edit.display'; 2 | export default [ 3 | ...common, 4 | { 5 | key: 'refreshOnChange', 6 | ignore: true 7 | }, 8 | { 9 | key: 'className', 10 | ignore: true, 11 | }, 12 | { 13 | key: 'prefix', 14 | ignore: true 15 | }, 16 | { 17 | key: 'suffix', 18 | ignore: true 19 | }, 20 | { 21 | key: 'labelPosition', 22 | ignore: true, 23 | }, 24 | { 25 | key: 'placeholder', 26 | ignore: true, 27 | }, 28 | ]; 29 | -------------------------------------------------------------------------------- /components/src/components/SimpleCheckbox/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Simple.edit.validation'; 2 | export default [ 3 | ...common, 4 | ]; 5 | -------------------------------------------------------------------------------- /components/src/components/SimpleCheckboxAdvanced/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/checkbox/Checkbox.form'; 2 | import EditValidation from './editForm/Component.edit.validation'; 3 | 4 | export default function(...extend) { 5 | return baseEditForm([ 6 | { 7 | key: 'validation', 8 | ignore: true 9 | }, 10 | { 11 | label: 'Validation', 12 | key: 'customValidation', 13 | weight: 15, 14 | components: EditValidation 15 | } 16 | ], ...extend); 17 | } 18 | -------------------------------------------------------------------------------- /components/src/components/SimpleCheckboxAdvanced/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Advanced.edit.validation'; 2 | import validationComponents from 'formiojs/components/checkbox/editForm/Checkbox.edit.validation'; 3 | import {reArrangeComponents} from '../../Common/function'; 4 | 5 | const neededposition = [ 6 | 'validate.isUseForCopy', 7 | 'validate.required', 8 | 'errorLabel', 9 | 'validate.customMessage', 10 | 'custom-validation-js', 11 | 'json-validation-json', 12 | 'errors', 13 | ]; 14 | 15 | const newPosition = reArrangeComponents(neededposition,[...validationComponents,...common]); 16 | 17 | 18 | export default newPosition; -------------------------------------------------------------------------------- /components/src/components/SimpleCheckboxes/editForm/Component.edit.display.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Simple.edit.display'; 2 | export default { 3 | key: 'display', 4 | components: 5 | [ 6 | ...common, 7 | { 8 | key: 'refreshOnChange', 9 | ignore: true 10 | }, 11 | { 12 | key: 'className', 13 | ignore: true, 14 | }, 15 | { 16 | key: 'attrs', 17 | ignore: true 18 | }, 19 | { 20 | key: 'widget', 21 | ignore: true 22 | }, 23 | { 24 | key: 'uniqueOptions', 25 | ignore: true 26 | }, 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /components/src/components/SimpleContent/Component.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | import { Components } from 'formiojs'; 3 | const ParentComponent = (Components as any).components.content; 4 | import editForm from './Component.form'; 5 | 6 | import { Constants } from '../Common/Constants'; 7 | 8 | const ID = 'simplecontent'; 9 | const DISPLAY = 'Text/Images'; 10 | 11 | export default class Component extends (ParentComponent as any) { 12 | static schema(...extend) { 13 | return ParentComponent.schema({ 14 | type: ID, 15 | label: DISPLAY, 16 | key: ID, 17 | input: false, 18 | html: '' 19 | }, ...extend); 20 | } 21 | 22 | public static editForm = editForm; 23 | 24 | static get builderInfo() { 25 | return { 26 | title: DISPLAY, 27 | group: 'simple', 28 | icon: 'pencil-square-o', 29 | weight: 40, 30 | documentation: Constants.DEFAULT_HELP_LINK, 31 | schema: Component.schema() 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /components/src/components/SimpleCurrencyAdvanced/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/currency/Currency.form'; 2 | import EditValidation from './editForm/Component.edit.validation'; 3 | 4 | export default function(...extend) { 5 | return baseEditForm([ 6 | { 7 | key: 'validation', 8 | ignore: true 9 | }, 10 | { 11 | label: 'Validation', 12 | key: 'customValidation', 13 | weight: 15, 14 | components: EditValidation 15 | } 16 | ], ...extend); 17 | } 18 | -------------------------------------------------------------------------------- /components/src/components/SimpleCurrencyAdvanced/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Advanced.edit.validation'; 2 | import {reArrangeComponents} from '../../Common/function'; 3 | 4 | const neededposition = [ 5 | 'validate.isUseForCopy', 6 | 'validateOn', 7 | 'validate.required', 8 | 'unique', 9 | 'errorLabel', 10 | 'validate.customMessage', 11 | 'custom-validation-js', 12 | 'json-validation-json', 13 | 'errors', 14 | ]; 15 | 16 | const newPosition = reArrangeComponents(neededposition,[...common]); 17 | 18 | 19 | export default newPosition; -------------------------------------------------------------------------------- /components/src/components/SimpleDateTime/editForm/Component.edit.data.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Simple.edit.data'; 2 | export default [ 3 | ...common, 4 | ]; 5 | -------------------------------------------------------------------------------- /components/src/components/SimpleDateTime/editForm/Component.edit.display.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Simple.edit.display'; 2 | export default [ 3 | ...common, 4 | { 5 | key: 'refreshOnChange', 6 | ignore: true 7 | }, 8 | { 9 | key: 'className', 10 | ignore: true, 11 | }, 12 | { 13 | key: 'prefix', 14 | ignore: true 15 | }, 16 | { 17 | key: 'suffix', 18 | ignore: true 19 | }, 20 | { 21 | key: 'inputMask', 22 | ignore: true 23 | }, 24 | { 25 | key: 'allowMultipleMasks', 26 | ignore: true 27 | }, 28 | { 29 | key: 'showWordCount', 30 | ignore: true, 31 | }, 32 | { 33 | key: 'showCharCount', 34 | ignore: true, 35 | }, 36 | ]; 37 | -------------------------------------------------------------------------------- /components/src/components/SimpleDateTimeAdvanced/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/datetime/DateTime.form'; 2 | import EditValidation from './editForm/Component.edit.validation'; 3 | 4 | export default function(...extend) { 5 | return baseEditForm([ 6 | { 7 | key: 'validation', 8 | ignore: true 9 | }, 10 | { 11 | label: 'Validation', 12 | key: 'customValidation', 13 | weight: 15, 14 | components: EditValidation 15 | } 16 | ], ...extend); 17 | } 18 | -------------------------------------------------------------------------------- /components/src/components/SimpleDateTimeAdvanced/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Advanced.edit.validation'; 2 | import validationComponents from 'formiojs/components/datetime/editForm/DateTime.edit.validation'; 3 | import {reArrangeComponents} from '../../Common/function'; 4 | 5 | const neededposition = [ 6 | 'validate.isUseForCopy', 7 | 'validateOn', 8 | 'validate.required', 9 | 'enableMinDateInput', 10 | 'datePicker.minDate', 11 | 'enableMaxDateInput', 12 | 'datePicker.maxDate', 13 | 'unique', 14 | 'errorLabel', 15 | 'validate.customMessage', 16 | 'custom-validation-js', 17 | 'json-validation-json', 18 | 'errors', 19 | ]; 20 | 21 | const newPosition = reArrangeComponents(neededposition,[...validationComponents,...common]); 22 | 23 | 24 | export default newPosition; -------------------------------------------------------------------------------- /components/src/components/SimpleDay/editForm/Component.edit.data.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | type: 'textfield', 4 | label: 'Default Value', 5 | key: 'defaultValue', 6 | weight: 5, 7 | placeholder: 'Default Value', 8 | tooltip: 'The will be the value for this field, before user interaction. Having a default value will override the placeholder text.', 9 | input: true 10 | }, 11 | ]; 12 | -------------------------------------------------------------------------------- /components/src/components/SimpleDay/editForm/Component.edit.month.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | wieght: 200, 4 | type: 'select', 5 | datasrc: 'values', 6 | key: 'fields.month.type', 7 | label: 'Type of input', 8 | data: { 9 | values: [ 10 | { 11 | label: 'Number', 12 | value: 'number' 13 | }, 14 | { 15 | label: 'Select', 16 | value: 'select' 17 | }, 18 | ] 19 | } 20 | }, 21 | { 22 | weight: 210, 23 | type: 'textfield', 24 | input: true, 25 | key: 'fields.month.placeholder', 26 | label: 'Placeholder', 27 | placeholder: 'Month Placeholder', 28 | tooltip: 'The placeholder text that will appear when Month field is empty.' 29 | }, 30 | { 31 | weight: 215, 32 | type: 'checkbox', 33 | label: 'Hidden', 34 | tooltip: 'Hide the Month part of the component.', 35 | key: 'fields.month.hide', 36 | input: true 37 | }, 38 | ]; 39 | -------------------------------------------------------------------------------- /components/src/components/SimpleDayAdvanced/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/day/Day.form'; 2 | import EditValidation from './editForm/Component.edit.validation'; 3 | 4 | export default function(...extend) { 5 | return baseEditForm([ 6 | { 7 | key: 'validation', 8 | ignore: true 9 | }, 10 | { 11 | label: 'Validation', 12 | key: 'customValidation', 13 | weight: 15, 14 | components: EditValidation 15 | } 16 | ], ...extend); 17 | } 18 | -------------------------------------------------------------------------------- /components/src/components/SimpleDayAdvanced/Component.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | import { Components } from 'formiojs'; 3 | const ParentComponent = (Components as any).components.day; 4 | import editForm from './Component.form'; 5 | 6 | import { Constants } from '../Common/Constants'; 7 | 8 | const ID = 'simpledayadvanced'; 9 | const DISPLAY = 'Day'; 10 | export default class Component extends (ParentComponent as any) { 11 | static schema(...extend) { 12 | return ParentComponent.schema({ 13 | type: ID, 14 | label: DISPLAY, 15 | key: ID, 16 | }, ...extend); 17 | } 18 | 19 | public static editForm = editForm; 20 | 21 | static get builderInfo() { 22 | return { 23 | title: DISPLAY, 24 | group: 'advanced', 25 | icon: 'calendar', 26 | weight: 790, 27 | documentation: Constants.DEFAULT_HELP_LINK, 28 | schema: Component.schema() 29 | }; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /components/src/components/SimpleDayAdvanced/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Advanced.edit.validation'; 2 | import validationComponents from 'formiojs/components/day/editForm/Day.edit.validation'; 3 | import {reArrangeComponents} from '../../Common/function'; 4 | 5 | const neededposition = [ 6 | 'validate.isUseForCopy', 7 | 'validateOn', 8 | 'fields.day.required', 9 | 'fields.month.required', 10 | 'fields.year.required', 11 | 'maxDate', 12 | 'minDate', 13 | 'unique', 14 | 'errorLabel', 15 | 'validate.customMessage', 16 | 'custom-validation-js', 17 | 'json-validation-json', 18 | 'errors', 19 | ]; 20 | 21 | const newPosition = reArrangeComponents(neededposition,[...validationComponents,...common]); 22 | 23 | 24 | export default newPosition; -------------------------------------------------------------------------------- /components/src/components/SimpleEmail/editForm/Component.edit.data.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Simple.edit.data'; 2 | export default [ 3 | ...common, 4 | { 5 | weight: 200, 6 | type: 'radio', 7 | label: 'Text Case', 8 | key: 'case', 9 | tooltip: 'When data is entered, you can change the case of the value.', 10 | input: true, 11 | values: [ 12 | { 13 | value: 'mixed', 14 | label: 'Mixed (Allow upper and lower case)' 15 | }, 16 | { 17 | value: 'uppercase', 18 | label: 'Uppercase' 19 | }, 20 | { 21 | value: 'lowercase', 22 | label: 'Lowercase' 23 | } 24 | ] 25 | } 26 | ]; 27 | -------------------------------------------------------------------------------- /components/src/components/SimpleEmail/editForm/Component.edit.display.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Simple.edit.display'; 2 | export default { 3 | key: 'display', 4 | components: [ 5 | ...common, 6 | { 7 | key: 'refreshOnChange', 8 | ignore: true 9 | }, 10 | { 11 | key: 'className', 12 | ignore: true, 13 | }, 14 | { 15 | key: 'prefix', 16 | ignore: true 17 | }, 18 | { 19 | key: 'suffix', 20 | ignore: true 21 | }, 22 | { 23 | key: 'inputMask', 24 | ignore: true 25 | }, 26 | { 27 | key: 'allowMultipleMasks', 28 | ignore: true 29 | }, 30 | { 31 | key: 'showWordCount', 32 | ignore: true, 33 | }, 34 | { 35 | key: 'showCharCount', 36 | ignore: true, 37 | }, 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /components/src/components/SimpleEmailAdvanced/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/email/Email.form'; 2 | import EditValidation from './editForm/Component.edit.validation'; 3 | 4 | export default function(...extend) { 5 | return baseEditForm([ 6 | { 7 | key: 'validation', 8 | ignore: true 9 | }, 10 | { 11 | label: 'Validation', 12 | key: 'customValidation', 13 | weight: 20, 14 | components: EditValidation 15 | } 16 | ], ...extend); 17 | } 18 | -------------------------------------------------------------------------------- /components/src/components/SimpleEmailAdvanced/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Advanced.edit.validation'; 2 | import {reArrangeComponents} from '../../Common/function'; 3 | 4 | const neededposition = [ 5 | 'validate.isUseForCopy', 6 | 'validateOn', 7 | 'validate.required', 8 | 'unique', 9 | 'kickbox', 10 | 'validate.minLength', 11 | 'validate.maxLength', 12 | 'validate.pattern', 13 | 'errorLabel', 14 | 'validate.customMessage', 15 | 'errors', 16 | 'custom-validation-js', 17 | 'json-validation-json' 18 | ]; 19 | 20 | const newPosition = reArrangeComponents(neededposition,common); 21 | export default newPosition; -------------------------------------------------------------------------------- /components/src/components/SimpleFile/editForm/Component.edit.data.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | weight: 0, 4 | type: 'checkbox', 5 | label: 'Multiple Values', 6 | tooltip: 'Allows multiple values to be entered for this field.', 7 | key: 'multiple', 8 | input: true 9 | }, 10 | ]; 11 | -------------------------------------------------------------------------------- /components/src/components/SimpleFile/editForm/Component.edit.display.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Simple.edit.display'; 2 | export default [ 3 | ...common, 4 | { 5 | key: 'placeholder', 6 | ignore: true 7 | }, 8 | { 9 | key: 'refreshOnChange', 10 | ignore: true 11 | }, 12 | { 13 | key: 'className', 14 | ignore: true, 15 | }, 16 | { 17 | key: 'prefix', 18 | ignore: true 19 | }, 20 | { 21 | key: 'suffix', 22 | ignore: true 23 | }, 24 | { 25 | key: 'inputMask', 26 | ignore: true 27 | }, 28 | { 29 | key: 'allowMultipleMasks', 30 | ignore: true 31 | }, 32 | { 33 | key: 'showWordCount', 34 | ignore: true, 35 | }, 36 | { 37 | key: 'showCharCount', 38 | ignore: true, 39 | }, 40 | ]; 41 | -------------------------------------------------------------------------------- /components/src/components/SimpleHeading/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/_classes/component/Component.form'; 2 | 3 | import EditDisplay from './editForm/Component.edit.display'; 4 | 5 | export default function(...extend) { 6 | return baseEditForm([ 7 | { 8 | key: 'display', 9 | components: EditDisplay 10 | 11 | }, 12 | { 13 | key: 'data', 14 | ignore: true 15 | }, 16 | { 17 | key: 'validation', 18 | ignore: true 19 | }, 20 | { 21 | key: 'api', 22 | ignore: true 23 | }, 24 | { 25 | key: 'conditional', 26 | ignore: true 27 | }, 28 | { 29 | key: 'layout', 30 | ignore: true 31 | } 32 | ], ...extend); 33 | } 34 | -------------------------------------------------------------------------------- /components/src/components/SimpleNumber/editForm/Component.edit.data.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Simple.edit.data'; 2 | export default [ 3 | ...common, 4 | { 5 | type: 'checkbox', 6 | input: true, 7 | weight: 70, 8 | key: 'delimiter', 9 | label: 'Use Thousands Separator', 10 | tooltip: 'Separate thousands by local delimiter.' 11 | }, 12 | { 13 | type: 'number', 14 | input: true, 15 | weight: 80, 16 | key: 'decimalLimit', 17 | label: 'Decimal Places', 18 | tooltip: 'The maximum number of decimal places.' 19 | }, 20 | { 21 | type: 'checkbox', 22 | input: true, 23 | weight: 90, 24 | key: 'requireDecimal', 25 | label: 'Require Decimal', 26 | tooltip: 'Always show decimals, even if trailing zeros.' 27 | }, 28 | ]; 29 | -------------------------------------------------------------------------------- /components/src/components/SimpleNumber/editForm/Component.edit.display.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Simple.edit.display'; 2 | export default { 3 | key: 'display', 4 | components: [ 5 | ...common, 6 | { 7 | key: 'refreshOnChange', 8 | ignore: true 9 | }, 10 | { 11 | key: 'className', 12 | ignore: true, 13 | }, 14 | { 15 | key: 'prefix', 16 | ignore: true 17 | }, 18 | { 19 | key: 'suffix', 20 | ignore: true 21 | }, 22 | { 23 | key: 'inputMask', 24 | ignore: true 25 | }, 26 | { 27 | key: 'allowMultipleMasks', 28 | ignore: true 29 | }, 30 | { 31 | key: 'showWordCount', 32 | ignore: true, 33 | }, 34 | { 35 | key: 'showCharCount', 36 | ignore: true, 37 | }, 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /components/src/components/SimpleNumber/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Simple.edit.validation'; 2 | export default [ 3 | ...common, 4 | { 5 | type: 'number', 6 | label: 'Minimum Value', 7 | key: 'validate.min', 8 | input: true, 9 | placeholder: 'Minimum Value', 10 | tooltip: 'The minimum value this field must have before the form can be submitted.', 11 | weight: 150 12 | }, 13 | { 14 | type: 'number', 15 | label: 'Maximum Value', 16 | key: 'validate.max', 17 | input: true, 18 | placeholder: 'Maximum Value', 19 | tooltip: 'The maximum value this field can have before the form can be submitted.', 20 | weight: 160 21 | } 22 | ]; 23 | -------------------------------------------------------------------------------- /components/src/components/SimpleNumberAdvanced/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/number/Number.form'; 2 | import EditValidation from './editForm/Component.edit.validation'; 3 | 4 | export default function(...extend) { 5 | return baseEditForm([ 6 | { 7 | key: 'validation', 8 | ignore: true 9 | }, 10 | { 11 | label: 'Validation', 12 | key: 'customValidation', 13 | weight: 15, 14 | components: EditValidation 15 | } 16 | ], ...extend); 17 | } 18 | -------------------------------------------------------------------------------- /components/src/components/SimpleNumberAdvanced/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Advanced.edit.validation'; 2 | import NumberEditValidation from 'formiojs/components/number/editForm/Number.edit.validation'; 3 | import {reArrangeComponents} from '../../Common/function'; 4 | 5 | const neededposition = [ 6 | 'validate.isUseForCopy', 7 | 'validateOn', 8 | 'validate.required', 9 | 'validate.min', 10 | 'validate.max', 11 | 'errorLabel', 12 | 'validate.customMessage', 13 | 'errors', 14 | 'custom-validation-js', 15 | 'json-validation-json' 16 | ]; 17 | 18 | const newPosition = reArrangeComponents(neededposition,[...NumberEditValidation, ...common]); 19 | 20 | export default newPosition; -------------------------------------------------------------------------------- /components/src/components/SimpleParagraph/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/_classes/component/Component.form'; 2 | 3 | import EditDisplay from './editForm/Component.edit.display'; 4 | 5 | export default function(...extend) { 6 | return baseEditForm([ 7 | { 8 | key: 'display', 9 | components: EditDisplay 10 | 11 | }, 12 | { 13 | key: 'data', 14 | ignore: true 15 | }, 16 | { 17 | key: 'validation', 18 | ignore: true 19 | }, 20 | { 21 | key: 'api', 22 | ignore: true 23 | }, 24 | { 25 | key: 'conditional', 26 | ignore: true 27 | }, 28 | { 29 | key: 'layout', 30 | ignore: true 31 | } 32 | ], ...extend); 33 | } 34 | -------------------------------------------------------------------------------- /components/src/components/SimplePasswordAdvanced/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/password/Password.form'; 2 | import EditValidation from './editForm/Component.edit.validation'; 3 | 4 | export default function(...extend) { 5 | return baseEditForm([ 6 | { 7 | key: 'validation', 8 | ignore: true 9 | }, 10 | { 11 | label: 'Validation', 12 | key: 'customValidation', 13 | weight: 20, 14 | components: EditValidation 15 | } 16 | ], ...extend); 17 | } 18 | -------------------------------------------------------------------------------- /components/src/components/SimplePasswordAdvanced/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Advanced.edit.validation'; 2 | import {reArrangeComponents} from '../../Common/function'; 3 | 4 | const neededposition = [ 5 | 'validate.isUseForCopy', 6 | 'validateOn', 7 | 'validate.required', 8 | 'validate.minLength', 9 | 'validate.maxLength', 10 | 'validate.pattern', 11 | 'errorLabel', 12 | 'validate.customMessage', 13 | 'custom-validation-js', 14 | 'json-validation-json', 15 | 'errors', 16 | ]; 17 | 18 | const newPosition = reArrangeComponents(neededposition,common); 19 | 20 | export default newPosition; 21 | -------------------------------------------------------------------------------- /components/src/components/SimplePhoneNumber/editForm/Component.edit.data.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Simple.edit.data'; 2 | export default [ 3 | ...common, 4 | ]; 5 | -------------------------------------------------------------------------------- /components/src/components/SimplePhoneNumberAdvanced/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/phonenumber/PhoneNumber.form'; 2 | import EditValidation from './editForm/Component.edit.validation'; 3 | 4 | export default function(...extend) { 5 | return baseEditForm([ 6 | { 7 | key: 'validation', 8 | ignore: true 9 | }, 10 | { 11 | label: 'Validation', 12 | key: 'customValidation', 13 | weight: 15, 14 | components: EditValidation 15 | } 16 | ], ...extend); 17 | } 18 | -------------------------------------------------------------------------------- /components/src/components/SimplePhoneNumberAdvanced/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Advanced.edit.validation'; 2 | import {reArrangeComponents} from '../../Common/function'; 3 | 4 | const neededposition = [ 5 | 'validate.isUseForCopy', 6 | 'validateOn', 7 | 'validate.required', 8 | 'unique', 9 | 'errorLabel', 10 | 'validate.customMessage', 11 | 'custom-validation-js', 12 | 'json-validation-json', 13 | 'errors' 14 | ]; 15 | 16 | const newPosition = reArrangeComponents(neededposition,common); 17 | 18 | export default newPosition; -------------------------------------------------------------------------------- /components/src/components/SimpleRadioAdvanced/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/radio/Radio.form'; 2 | import EditValidation from './editForm/Component.edit.validation'; 3 | 4 | export default function(...extend) { 5 | return baseEditForm([ 6 | { 7 | key: 'validation', 8 | ignore: true 9 | }, 10 | { 11 | label: 'Validation', 12 | key: 'customValidation', 13 | weight: 15, 14 | components: EditValidation 15 | } 16 | ], ...extend); 17 | } 18 | -------------------------------------------------------------------------------- /components/src/components/SimpleRadioAdvanced/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Advanced.edit.validation'; 2 | import {reArrangeComponents} from '../../Common/function'; 3 | import validationComponents from 'formiojs/components/radio/editForm/Radio.edit.validation'; 4 | 5 | const neededposition = [ 6 | 'validate.isUseForCopy', 7 | 'validate.required', 8 | 'validate.onlyAvailableItems', 9 | 'errorLabel', 10 | 'validate.customMessage', 11 | 'custom-validation-js', 12 | 'json-validation-json', 13 | 'errors', 14 | ]; 15 | 16 | 17 | const newPosition = reArrangeComponents(neededposition,[...validationComponents,...common]); 18 | 19 | 20 | export default newPosition; -------------------------------------------------------------------------------- /components/src/components/SimpleRadios/editForm/Component.edit.display.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Simple.edit.display'; 2 | export default { 3 | key: 'display', 4 | components: [ 5 | ...common, 6 | { 7 | key: 'refreshOnChange', 8 | ignore: true 9 | }, 10 | { 11 | key: 'className', 12 | ignore: true, 13 | }, 14 | { 15 | key: 'attrs', 16 | ignore: true 17 | }, 18 | { 19 | key: 'widget', 20 | ignore: true 21 | }, 22 | { 23 | key: 'uniqueOptions', 24 | ignore: true 25 | }, 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /components/src/components/SimpleRadios/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Simple.edit.validation'; 2 | 3 | export default [ 4 | ...common 5 | ]; 6 | -------------------------------------------------------------------------------- /components/src/components/SimpleSelect/editForm/Component.edit.display.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Simple.edit.display'; 2 | export default { 3 | key: 'display', 4 | components: [ 5 | ...common, 6 | { 7 | key: 'refreshOnChange', 8 | ignore: true 9 | }, 10 | { 11 | key: 'className', 12 | ignore: true, 13 | }, 14 | { 15 | key: 'attrs', 16 | ignore: true 17 | }, 18 | { 19 | key: 'widget', 20 | ignore: true 21 | }, 22 | { 23 | key: 'uniqueOptions', 24 | ignore: true 25 | }, 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /components/src/components/SimpleSelect/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Simple.edit.validation'; 2 | export default [ 3 | ...common, 4 | ]; 5 | -------------------------------------------------------------------------------- /components/src/components/SimpleSelectAdvanced/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/select/Select.form'; 2 | import EditValidation from './editForm/Component.edit.validation'; 3 | 4 | export default function(...extend) { 5 | return baseEditForm([ 6 | { 7 | key: 'validation', 8 | ignore: true 9 | }, 10 | { 11 | label: 'Validation', 12 | key: 'customValidation', 13 | weight: 15, 14 | components: EditValidation 15 | } 16 | ], ...extend); 17 | } 18 | -------------------------------------------------------------------------------- /components/src/components/SimpleSelectBoxesAdvanced/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/selectboxes/SelectBoxes.form'; 2 | import EditValidation from './editForm/Component.edit.validation'; 3 | 4 | export default function(...extend) { 5 | return baseEditForm([ 6 | { 7 | key: 'validation', 8 | ignore: true 9 | }, 10 | { 11 | label: 'Validation', 12 | key: 'customValidation', 13 | weight: 15, 14 | components: EditValidation 15 | } 16 | ], ...extend); 17 | } 18 | -------------------------------------------------------------------------------- /components/src/components/SimpleSignatureAdvanced/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/signature/Signature.form'; 2 | import EditValidation from './editForm/Component.edit.validation'; 3 | 4 | export default function(...extend) { 5 | return baseEditForm([ 6 | { 7 | key: 'validation', 8 | ignore: true 9 | }, 10 | { 11 | label: 'Validation', 12 | key: 'customValidation', 13 | weight: 15, 14 | components: EditValidation 15 | } 16 | ], ...extend); 17 | } 18 | -------------------------------------------------------------------------------- /components/src/components/SimpleSignatureAdvanced/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Advanced.edit.validation'; 2 | import {reArrangeComponents} from '../../Common/function'; 3 | 4 | const neededposition = [ 5 | 'validate.isUseForCopy', 6 | 'validate.required', 7 | 'errorLabel', 8 | 'validate.customMessage', 9 | 'custom-validation-js', 10 | 'json-validation-json', 11 | 'errors', 12 | ]; 13 | 14 | 15 | const newPosition = reArrangeComponents(neededposition,[...common]); 16 | 17 | 18 | export default newPosition; -------------------------------------------------------------------------------- /components/src/components/SimpleSurveyAdvanced/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/survey/Survey.form'; 2 | import EditValidation from './editForm/Component.edit.validation'; 3 | 4 | export default function(...extend) { 5 | return baseEditForm([ 6 | { 7 | key: 'validation', 8 | ignore: true 9 | }, 10 | { 11 | label: 'Validation', 12 | key: 'customValidation', 13 | weight: 15, 14 | components: EditValidation 15 | } 16 | ], ...extend); 17 | } 18 | -------------------------------------------------------------------------------- /components/src/components/SimpleSurveyAdvanced/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Advanced.edit.validation'; 2 | import {reArrangeComponents} from '../../Common/function'; 3 | 4 | const neededposition = [ 5 | 'validate.isUseForCopy', 6 | 'validate.required', 7 | 'unique', 8 | 'errorLabel', 9 | 'validate.customMessage', 10 | 'custom-validation-js', 11 | 'json-validation-json', 12 | 'errors', 13 | ]; 14 | 15 | 16 | const newPosition = reArrangeComponents(neededposition,[...common]); 17 | 18 | 19 | export default newPosition; -------------------------------------------------------------------------------- /components/src/components/SimpleTagsAdvanced/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/tags/Tags.form'; 2 | import EditValidation from './editForm/Component.edit.validation'; 3 | 4 | export default function(...extend) { 5 | return baseEditForm([ 6 | { 7 | key: 'validation', 8 | ignore: true 9 | }, 10 | { 11 | label: 'Validation', 12 | key: 'customValidation', 13 | weight: 15, 14 | components: EditValidation 15 | } 16 | ], ...extend); 17 | } 18 | -------------------------------------------------------------------------------- /components/src/components/SimpleTagsAdvanced/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Advanced.edit.validation'; 2 | import {reArrangeComponents} from '../../Common/function'; 3 | 4 | const neededposition = [ 5 | 'validate.isUseForCopy', 6 | 'validate.required', 7 | 'unique', 8 | 'validateOn', 9 | 'errorLabel', 10 | 'validate.customMessage', 11 | 'custom-validation-js', 12 | 'json-validation-json', 13 | 'errors', 14 | ]; 15 | 16 | const newPosition = reArrangeComponents(neededposition,common); 17 | 18 | export default newPosition; -------------------------------------------------------------------------------- /components/src/components/SimpleTextAreaAdvanced/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/textarea/TextArea.form'; 2 | import EditValidation from './editForm/Component.edit.validation'; 3 | 4 | export default function(...extend) { 5 | return baseEditForm([ 6 | { 7 | key: 'validation', 8 | ignore: true 9 | }, 10 | { 11 | label: 'Validation', 12 | key: 'customValidation', 13 | weight: 20, 14 | components: EditValidation 15 | } 16 | ], ...extend); 17 | } 18 | -------------------------------------------------------------------------------- /components/src/components/SimpleTextAreaAdvanced/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Advanced.edit.validation'; 2 | import {reArrangeComponents} from '../../Common/function'; 3 | 4 | const neededposition = [ 5 | 'validate.isUseForCopy', 6 | 'validateOn', 7 | 'validate.required', 8 | 'unique', 9 | 'validate.minLength', 10 | 'validate.maxLength', 11 | 'validate.minWords', 12 | 'validate.maxWords', 13 | 'validate.pattern', 14 | 'errorLabel', 15 | 'validate.customMessage', 16 | 'errors', 17 | 'custom-validation-js', 18 | 'json-validation-json' 19 | ]; 20 | 21 | const newPosition = reArrangeComponents(neededposition,common); 22 | 23 | export default newPosition; -------------------------------------------------------------------------------- /components/src/components/SimpleTextField/editForm/Component.edit.data.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Simple.edit.data'; 2 | export default [ 3 | ...common, 4 | { 5 | weight: 200, 6 | type: 'radio', 7 | label: 'Text Case', 8 | key: 'case', 9 | tooltip: 'When data is entered, you can change the case of the value.', 10 | input: true, 11 | values: [ 12 | { 13 | value: 'mixed', 14 | label: 'Mixed (Allow upper and lower case)' 15 | }, 16 | { 17 | value: 'uppercase', 18 | label: 'Uppercase' 19 | },{ 20 | value: 'lowercase', 21 | label: 'Lowercase' 22 | } 23 | ] 24 | } 25 | ]; 26 | -------------------------------------------------------------------------------- /components/src/components/SimpleTextField/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Simple.edit.validation'; 2 | export default [ 3 | ...common, 4 | { 5 | weight: 110, 6 | key: 'validate.minLength', 7 | label: 'Minimum Length', 8 | placeholder: 'Minimum Length', 9 | type: 'number', 10 | tooltip: 'The minimum length requirement this field must meet.', 11 | input: true 12 | }, 13 | { 14 | weight: 120, 15 | key: 'validate.maxLength', 16 | label: 'Maximum Length', 17 | placeholder: 'Maximum Length', 18 | type: 'number', 19 | tooltip: 'The maximum length requirement this field must meet.', 20 | input: true 21 | } 22 | ]; 23 | -------------------------------------------------------------------------------- /components/src/components/SimpleTextFieldAdvanced/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/textfield/TextField.form'; 2 | import EditValidation from './editForm/Component.edit.validation'; 3 | import EditDisplay from './editForm/Component.edit.display'; 4 | 5 | export default function(...extend) { 6 | return baseEditForm([ 7 | { 8 | key: 'display', 9 | ignore: true 10 | }, 11 | { 12 | key: 'validation', 13 | ignore: true 14 | }, 15 | { 16 | label: 'Display', 17 | key: 'customDisplay', 18 | weight: 5, 19 | components: EditDisplay 20 | }, 21 | { 22 | label: 'Validation', 23 | key: 'customValidation', 24 | weight: 15, 25 | components: EditValidation 26 | } 27 | ], ...extend); 28 | } 29 | -------------------------------------------------------------------------------- /components/src/components/SimpleTextFieldAdvanced/editForm/Component.edit.display.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Advanced.edit.display'; 2 | 3 | import {reArrangeComponents} from '../../Common/function'; 4 | 5 | const neededposition = [ 6 | 'label', 7 | 'labelPosition', 8 | 'labelWidth', 9 | 'labelMargin', 10 | 'placeholder', 11 | 'description', 12 | 'tooltip', 13 | 'prefix', 14 | 'suffix', 15 | 'widget.type', 16 | 'widget', 17 | 'inputMask', 18 | 'displayMask', 19 | 'inputMaskPlaceholderChar', 20 | 'allowMultipleMasks', 21 | 'customClass', 22 | 'tabindex', 23 | 'autocomplete', 24 | 'hidden', 25 | 'hideLabel', 26 | 'showWordCount', 27 | 'showCharCount', 28 | 'mask', 29 | 'autofocus', 30 | 'spellcheck', 31 | 'disabled', 32 | 'tableView', 33 | 'modalEdit' 34 | ]; 35 | 36 | const newPosition = reArrangeComponents(neededposition,common); 37 | 38 | export default newPosition; 39 | -------------------------------------------------------------------------------- /components/src/components/SimpleTextFieldAdvanced/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Advanced.edit.validation'; 2 | import {reArrangeComponents} from '../../Common/function'; 3 | 4 | const neededposition = [ 5 | 'validate.isUseForCopy', 6 | 'validateOn', 7 | 'validate.required', 8 | 'unique', 9 | 'validate.minLength', 10 | 'validate.maxLength', 11 | 'validate.minWords', 12 | 'validate.maxWords', 13 | 'validate.pattern', 14 | 'errorLabel', 15 | 'validate.customMessage', 16 | 'errors', 17 | 'custom-validation-js', 18 | 'json-validation-json' 19 | ]; 20 | 21 | const newPosition = reArrangeComponents(neededposition,common); 22 | 23 | export default newPosition; -------------------------------------------------------------------------------- /components/src/components/SimpleTime/editForm/Component.edit.data.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | type: 'textfield', 4 | label: 'Default Value', 5 | key: 'defaultValue', 6 | weight: 5, 7 | placeholder: 'Default Value', 8 | tooltip: 'The will be the value for this field, before user interaction. Having a default value will override the placeholder text.', 9 | input: true 10 | }, 11 | { 12 | type: 'textfield', 13 | input: true, 14 | key: 'dataFormat', 15 | label: 'Data Format', 16 | placeholder: 'HH:mm:ss', 17 | tooltip: 'The moment.js format for saving the value of this field.', 18 | weight: 25, 19 | }, 20 | 21 | ]; 22 | -------------------------------------------------------------------------------- /components/src/components/SimpleTimeAdvanced/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/time/Time.form'; 2 | import EditValidation from './editForm/Component.edit.validation'; 3 | 4 | export default function(...extend) { 5 | return baseEditForm([ 6 | { 7 | key: 'validation', 8 | ignore: true 9 | }, 10 | { 11 | label: 'Validation', 12 | key: 'customValidation', 13 | weight: 15, 14 | components: EditValidation 15 | } 16 | ], ...extend); 17 | } 18 | -------------------------------------------------------------------------------- /components/src/components/SimpleTimeAdvanced/Component.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | import { Components } from 'formiojs'; 3 | const ParentComponent = (Components as any).components.time; 4 | import editForm from './Component.form'; 5 | 6 | import { Constants } from '../Common/Constants'; 7 | 8 | const ID = 'simpletimeadvanced'; 9 | const DISPLAY = 'Time'; 10 | 11 | export default class Component extends (ParentComponent as any) { 12 | static schema(...extend) { 13 | return ParentComponent.schema({ 14 | type: ID, 15 | label: DISPLAY, 16 | key: ID, 17 | }, ...extend); 18 | } 19 | 20 | public static editForm = editForm; 21 | 22 | static get builderInfo() { 23 | return { 24 | title: DISPLAY, 25 | group: 'advanced', 26 | icon: 'clock-o', 27 | weight: 800, 28 | documentation: Constants.DEFAULT_HELP_LINK, 29 | schema: Component.schema() 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /components/src/components/SimpleTimeAdvanced/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Advanced.edit.validation'; 2 | import {reArrangeComponents} from '../../Common/function'; 3 | 4 | const neededposition = [ 5 | 'validate.isUseForCopy', 6 | 'validate.required', 7 | 'unique', 8 | 'validateOn', 9 | 'errorLabel', 10 | 'validate.customMessage', 11 | 'custom-validation-js', 12 | 'json-validation-json', 13 | 'errors', 14 | ]; 15 | 16 | const newPosition = reArrangeComponents(neededposition,common); 17 | 18 | 19 | export default newPosition; -------------------------------------------------------------------------------- /components/src/components/SimpleUrlAdvanced/Component.form.ts: -------------------------------------------------------------------------------- 1 | import baseEditForm from 'formiojs/components/url/Url.form'; 2 | import EditValidation from './editForm/Component.edit.validation'; 3 | 4 | export default function(...extend) { 5 | return baseEditForm([ 6 | { 7 | key: 'validation', 8 | ignore: true 9 | }, 10 | { 11 | label: 'Validation', 12 | key: 'customValidation', 13 | weight: 15, 14 | components: EditValidation 15 | } 16 | ], ...extend); 17 | } 18 | -------------------------------------------------------------------------------- /components/src/components/SimpleUrlAdvanced/editForm/Component.edit.validation.ts: -------------------------------------------------------------------------------- 1 | import common from '../../Common/Advanced.edit.validation'; 2 | import {reArrangeComponents} from '../../Common/function'; 3 | 4 | const neededposition = [ 5 | 'validate.isUseForCopy', 6 | 'validateOn', 7 | 'validate.required', 8 | 'unique', 9 | 'validate.minLength', 10 | 'validate.maxLength', 11 | 'validate.pattern', 12 | 'errorLabel', 13 | 'validate.customMessage', 14 | 'errors', 15 | 'custom-validation-js', 16 | 'json-validation-json' 17 | ]; 18 | 19 | const newPosition = reArrangeComponents(neededposition,common); 20 | 21 | export default newPosition; -------------------------------------------------------------------------------- /components/src/ejs.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.ejs' { 2 | const value: string; 3 | export default value 4 | } 5 | 6 | declare var Formio: any; 7 | -------------------------------------------------------------------------------- /components/src/index.ts: -------------------------------------------------------------------------------- 1 | import './overrides/editform/utils'; 2 | 3 | import components from './components'; 4 | 5 | export default { 6 | components, 7 | }; 8 | -------------------------------------------------------------------------------- /components/src/sass/contrib.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcgov/common-hosted-form-service/ee209435541c048da1bc081f42fd5008ae415c06/components/src/sass/contrib.scss -------------------------------------------------------------------------------- /components/src/use.ts: -------------------------------------------------------------------------------- 1 | import { Formio } from 'formiojs'; 2 | import BcGovFormioComponents from './index'; 3 | (Formio as any).use(BcGovFormioComponents); 4 | export default BcGovFormioComponents; 5 | -------------------------------------------------------------------------------- /components/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "preserveConstEnums": true, 5 | "outDir": "lib", 6 | "noImplicitAny": false, 7 | "sourceMap": false, 8 | "declaration": true, 9 | "rootDir": "src", 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "lib": [ 13 | "esnext", 14 | "dom" 15 | ], 16 | "types": ["vite/client"] 17 | }, 18 | "include": [ 19 | "src/*.ts", 20 | "src/*.ejs", 21 | "src/**/*.ts", 22 | "src/**/*.ejs" 23 | ], 24 | "exclude": [ 25 | "node_modules", 26 | "**/*.spec.ts" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /components/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": {}, 7 | "rules": { 8 | "quotemark": [true, "single", "avoid-escape", "avoid-template"], 9 | "one-line": [false, "check-catch", "check-finally", "check-else"], 10 | "object-literal-sort-keys": false, 11 | "no-shadowed-variable": false, 12 | "variable-name": { 13 | "options": [ 14 | "allow-leading-underscore", 15 | "allow-pascal-case", 16 | "ban-keywords" 17 | ] 18 | }, 19 | "max-classes-per-file": [false] 20 | }, 21 | "rulesDirectory": [], 22 | "exclude": [ 23 | "lib", 24 | "node_modules" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /components/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | module: { 5 | rules: [ 6 | { 7 | test: /\.css$/i, 8 | use: ["css-loader"], 9 | }, 10 | ], 11 | }, 12 | entry: path.join(path.resolve(__dirname, 'lib'), 'index.js'), 13 | output: { 14 | library: 'BcGovFormioComponents', 15 | libraryTarget: 'umd', 16 | libraryExport: 'default', 17 | path: path.resolve(__dirname, 'dist'), 18 | filename: 'bcgov-formio-components.js', 19 | }, 20 | mode: 'development', 21 | performance: { hints: false }, 22 | externals: { 23 | formiojs: 'Formio' 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /components/webpack.prod.js: -------------------------------------------------------------------------------- 1 | const config = require('./webpack.config.js'); 2 | config.mode = 'production'; 3 | config.output.filename = 'bcgov-formio-components.min.js'; 4 | module.exports = config; 5 | -------------------------------------------------------------------------------- /components/webpack.use.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const config = require('./webpack.config.js'); 3 | config.entry = path.join(path.resolve(__dirname, 'lib'), 'use.js'), 4 | config.mode = 'production'; 5 | config.output.filename = 'bcgov-formio-components.use.min.js'; 6 | module.exports = config; 7 | -------------------------------------------------------------------------------- /openshift/allow-from-openshift-ingress.np.yaml: -------------------------------------------------------------------------------- 1 | kind: NetworkPolicy 2 | apiVersion: networking.k8s.io/v1 3 | metadata: 4 | name: allow-from-openshift-ingress 5 | spec: 6 | podSelector: {} 7 | ingress: 8 | - from: 9 | - namespaceSelector: 10 | matchLabels: 11 | network.openshift.io/policy-group: ingress 12 | policyTypes: 13 | - Ingress 14 | status: {} 15 | -------------------------------------------------------------------------------- /openshift/app.dev.param: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcgov/common-hosted-form-service/ee209435541c048da1bc081f42fd5008ae415c06/openshift/app.dev.param -------------------------------------------------------------------------------- /openshift/app.prod.param: -------------------------------------------------------------------------------- 1 | CPU_LIMIT=1500m 2 | CPU_REQUEST=250m 3 | MEMORY_LIMIT=2Gi 4 | REPLICAS=3 5 | -------------------------------------------------------------------------------- /openshift/app.test.param: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcgov/common-hosted-form-service/ee209435541c048da1bc081f42fd5008ae415c06/openshift/app.test.param -------------------------------------------------------------------------------- /openshift/clamav.dev.param: -------------------------------------------------------------------------------- 1 | HOST=10.98.191.237 2 | PORT=3310 -------------------------------------------------------------------------------- /openshift/clamav.prod.param: -------------------------------------------------------------------------------- 1 | HOST=10.98.60.250 2 | PORT=3310 -------------------------------------------------------------------------------- /openshift/clamav.test.param: -------------------------------------------------------------------------------- 1 | HOST=10.98.132.77 2 | PORT=3310 -------------------------------------------------------------------------------- /openshift/crunchydb/README.md: -------------------------------------------------------------------------------- 1 | # CrunchyDB 2 | 3 | CHEFS uses CrunchyDB for all of its highly available databases. CrunchyDB uses 4 | Patroni for replication and failovers, and Patroni uses PostgreSQL as the 5 | underlying database. 6 | 7 | ## Installation 8 | 9 | The CrunchyDB installations use the 10 | [Helm charts](https://github.com/bcgov/crunchy-postgres) provided by the fine 11 | folks over at platform services. Huge thanks go to that team for doing the hard 12 | work of figuring out the CrunchyDB setup and making it easier for the community 13 | to use CrunchyDB. 14 | 15 | The `charts` directory has been copied here so that changes to the upstream repo 16 | don't unexpectedly change our deployments. This stability and consistency comes 17 | at the cost of added maintenance effort to stay in sync. 18 | 19 | This code is current to commit `91d32cb` in December 2024. 20 | -------------------------------------------------------------------------------- /openshift/crunchydb/charts/crunchy-postgres/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /openshift/crunchydb/charts/crunchy-postgres/templates/_s3.tpl: -------------------------------------------------------------------------------- 1 | {{/* Allow for S3 secret information to be stored in a Secret */}} 2 | {{- define "postgres.s3" }} 3 | [global] 4 | {{- if .s3 }} 5 | {{- if .s3.key }} 6 | repo{{ add .index 1 }}-s3-key={{ .s3.key }} 7 | {{- end }} 8 | {{- if .s3.keySecret }} 9 | repo{{ add .index 1 }}-s3-key-secret={{ .s3.keySecret }} 10 | {{- end }} 11 | {{- if .s3.keyType }} 12 | repo{{ add .index 1 }}-s3-key-type={{ .s3.keyType }} 13 | {{- end }} 14 | {{- if .s3.encryptionPassphrase }} 15 | repo{{ add .index 1 }}-cipher-pass={{ .s3.encryptionPassphrase }} 16 | {{- end }} 17 | {{- end }} 18 | {{ end }} -------------------------------------------------------------------------------- /openshift/crunchydb/charts/crunchy-postgres/templates/s3Secret.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.pgBackRest.s3.enabled .Values.pgBackRest.s3.createS3Secret }} 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: {{ .Values.pgBackRest.s3.s3Secret }} 6 | type: Opaque 7 | data: 8 | {{- $args := dict "s3" .Values.pgBackRest.s3 "index" 1 }} 9 | s3.conf: |- 10 | {{ include "postgres.s3" $args | b64enc }} 11 | {{- end }} -------------------------------------------------------------------------------- /openshift/crunchydb/charts/tools/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /openshift/crunchydb/charts/tools/templates/deployer/deployerRoleBinding.yaml: -------------------------------------------------------------------------------- 1 | {{ if and .Values.deployer.serviceAccount.enabled (ne .Release.Namespace .Values.provisioner.namespace) }} 2 | 3 | kind: RoleBinding 4 | apiVersion: rbac.authorization.k8s.io/v1 5 | metadata: 6 | name: {{ or .Values.deploymentName .Release.Name }}-deployer 7 | labels: 8 | {{ include "crunchy-postgres-tools.labels" . | indent 4}} 9 | roleRef: 10 | apiGroup: rbac.authorization.k8s.io 11 | kind: Role 12 | name: {{ or .Values.deploymentName .Release.Name }}-deployer 13 | subjects: 14 | - kind: ServiceAccount 15 | name: {{ or .Values.deploymentName .Release.Name }}-deployer 16 | namespace: {{ .Release.namespace }} 17 | 18 | {{ end }} 19 | -------------------------------------------------------------------------------- /openshift/crunchydb/charts/tools/templates/deployer/deployerServiceAccount.yaml: -------------------------------------------------------------------------------- 1 | {{ if and .Values.deployer.serviceAccount.enabled (ne .Release.Namespace .Values.provisioner.namespace) }} 2 | 3 | kind: ServiceAccount 4 | apiVersion: v1 5 | metadata: 6 | name: {{ or .Values.deploymentName .Release.Name }}-deployer 7 | labels: 8 | {{ include "crunchy-postgres-tools.labels" . | indent 4}} 9 | namespace: {{ .Release.namespace }} 10 | 11 | {{ end }} 12 | -------------------------------------------------------------------------------- /openshift/crunchydb/charts/tools/templates/linter/linterRoleBinding.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.linter.serviceAccount.enabled (eq .Release.Namespace .Values.provisioner.namespace) }} 2 | 3 | kind: RoleBinding 4 | apiVersion: rbac.authorization.k8s.io/v1 5 | metadata: 6 | name: {{ .Release.Name }}-linter 7 | labels: {{ include "crunchy-postgres-tools.labels" . | nindent 4}} 8 | roleRef: 9 | apiGroup: rbac.authorization.k8s.io 10 | kind: Role 11 | name: {{ .Release.Name }}-linter 12 | subjects: 13 | - kind: ServiceAccount 14 | name: {{ .Release.Name }}-linter 15 | namespace: {{ .Values.linter.namespace }} 16 | 17 | {{ end }} 18 | -------------------------------------------------------------------------------- /openshift/crunchydb/charts/tools/templates/linter/linterServiceAccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.linter.serviceAccount.enabled (eq .Release.Namespace .Values.provisioner.namespace)}} 2 | 3 | kind: ServiceAccount 4 | apiVersion: v1 5 | metadata: 6 | name: {{ .Release.Name }}-linter 7 | labels: {{ include "crunchy-postgres-tools.labels" . | nindent 4}} 8 | namespace: {{ .Values.linter.namespace }} 9 | 10 | {{ end }} 11 | -------------------------------------------------------------------------------- /openshift/crunchydb/charts/tools/templates/networking/networkPolicy.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.networking.networkPolicy.enabled (ne .Release.Namespace .Values.provisioner.namespace) }} 2 | 3 | kind: NetworkPolicy 4 | apiVersion: networking.k8s.io/v1 5 | metadata: 6 | name: {{ or .Values.deploymentName .Release.Name }}-allow-route-ingress 7 | labels: 8 | {{ include "crunchy-postgres-tools.labels" . | indent 4}} 9 | spec: 10 | # This policy allows any pod with a route & service combination 11 | # to accept traffic from the OpenShift router pods. This is 12 | # required for things outside of OpenShift (like the Internet) 13 | # to reach your pods. 14 | ingress: 15 | - from: 16 | - namespaceSelector: 17 | matchLabels: 18 | network.openshift.io/policy-group: ingress 19 | podSelector: {} 20 | policyTypes: 21 | - Ingress 22 | 23 | {{- end }} 24 | -------------------------------------------------------------------------------- /openshift/crunchydb/charts/tools/templates/networking/podNetworkPolicy.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.networking.podNetworkPolicy.enabled (ne .Release.Namespace .Values.provisioner.namespace) }} 2 | 3 | kind: NetworkPolicy 4 | apiVersion: networking.k8s.io/v1 5 | metadata: 6 | name: {{ or .Values.deploymentName .Release.Name }}-allow-same-namespace 7 | labels: 8 | {{ include "crunchy-postgres-tools.labels" . | indent 4}} 9 | spec: 10 | # This policy allows pods to accept traffic from other pods in this namespace 11 | ingress: 12 | - from: 13 | - podSelector: {} 14 | podSelector: {} 15 | 16 | {{ end }} 17 | -------------------------------------------------------------------------------- /openshift/crunchydb/charts/tools/templates/networking/route.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.networking.route.enabled (ne .Release.Namespace .Values.provisioner.namespace) }} 2 | 3 | apiVersion: route.openshift.io/v1 4 | kind: Route 5 | metadata: 6 | name: {{ template "crunchy-postgres-tools.fullname" . }} 7 | labels: 8 | {{ include "crunchy-postgres-tools.labels" . | indent 4}} 9 | spec: 10 | host: {{ .Values.networking.route.host }} 11 | port: 12 | targetPort: {{ template "crunchy-postgres-tools.fullname" . }} 13 | tls: 14 | termination: edge 15 | insecureEdgeTerminationPolicy: Redirect 16 | to: 17 | kind: Service 18 | name: {{ template "crunchy-postgres-tools.fullname" . }} 19 | weight: 100 20 | 21 | {{ end }} 22 | -------------------------------------------------------------------------------- /openshift/crunchydb/charts/tools/templates/provisioner/provisionerRoleBinding.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.provisioner.serviceAccount.enabled }} 2 | 3 | kind: RoleBinding 4 | apiVersion: rbac.authorization.k8s.io/v1 5 | metadata: 6 | name: {{ .Release.Name }}-provisioner 7 | labels: 8 | {{ include "crunchy-postgres-tools.labels" . | indent 4 }} 9 | roleRef: 10 | apiGroup: rbac.authorization.k8s.io 11 | kind: Role 12 | name: {{ .Release.Name }}-provisioner 13 | subjects: 14 | - kind: ServiceAccount 15 | name: {{ .Release.Name }}-provisioner 16 | namespace: {{ .Values.provisioner.namespace }} 17 | 18 | 19 | {{ end }} 20 | -------------------------------------------------------------------------------- /openshift/crunchydb/charts/tools/templates/provisioner/provisionerServiceAccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.provisioner.serviceAccount.enabled (eq .Release.Namespace .Values.provisioner.namespace) }} 2 | 3 | kind: ServiceAccount 4 | apiVersion: v1 5 | metadata: 6 | name: {{ .Release.Name }}-provisioner 7 | labels: 8 | {{ include "crunchy-postgres-tools.labels" . | indent 4 }} 9 | namespace: {{ .Values.provisioner.namespace }} 10 | 11 | {{ end }} 12 | -------------------------------------------------------------------------------- /openshift/ess.dev.param: -------------------------------------------------------------------------------- 1 | STREAMNAME=CHEFS 2 | SOURCE=chefs-dev 3 | DOMAIN=forms 4 | MAXAGE="604800000" 5 | MAXBYTES="966367641" 6 | MAXMSGS="1000" 7 | MAXMSGSIZE="966367" 8 | DUPLICATEWINDOW="60000" 9 | NUMREPLICAS="3" 10 | SERVERS=ess-nats.a191b5-dev.svc.cluster.local 11 | WEBSOCKETS=false 12 | CONSUMERSERVERS=stream-dev.apps.silver.devops.gov.bc.ca -------------------------------------------------------------------------------- /openshift/ess.prod.param: -------------------------------------------------------------------------------- 1 | STREAMNAME=CHEFS 2 | SOURCE=chefs 3 | DOMAIN=forms 4 | MAXAGE="604800000" 5 | MAXBYTES="966367641" 6 | MAXMSGS="1000" 7 | MAXMSGSIZE="966367" 8 | DUPLICATEWINDOW="60000" 9 | NUMREPLICAS="3" 10 | SERVERS=ess-nats.a191b5-prod.svc.cluster.local 11 | WEBSOCKETS=false 12 | CONSUMERSERVERS=stream.digital.gov.bc.ca -------------------------------------------------------------------------------- /openshift/ess.test.param: -------------------------------------------------------------------------------- 1 | STREAMNAME=CHEFS 2 | SOURCE=chefs-test 3 | DOMAIN=forms 4 | MAXAGE="900000" 5 | MAXBYTES="209715200" 6 | MAXMSGS="500" 7 | MAXMSGSIZE="419430" 8 | DUPLICATEWINDOW="60000" 9 | NUMREPLICAS="3" 10 | SERVERS=ess-nats.a191b5-test.svc.cluster.local 11 | WEBSOCKETS=false 12 | CONSUMERSERVERS=stream-test.apps.silver.devops.gov.bc.ca -------------------------------------------------------------------------------- /openshift/patroni.dev.param: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcgov/common-hosted-form-service/ee209435541c048da1bc081f42fd5008ae415c06/openshift/patroni.dev.param -------------------------------------------------------------------------------- /openshift/patroni.prod.param: -------------------------------------------------------------------------------- 1 | CPU_LIMIT=2 2 | MEMORY_LIMIT=2048Mi 3 | MEMORY_REQUEST=1536Mi 4 | -------------------------------------------------------------------------------- /openshift/patroni.test.param: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcgov/common-hosted-form-service/ee209435541c048da1bc081f42fd5008ae415c06/openshift/patroni.test.param -------------------------------------------------------------------------------- /openshift/redash/crunchydb-postgres-values-no-limits.yaml: -------------------------------------------------------------------------------- 1 | instances: 2 | limits: 3 | cpu: 4 | memory: 5 | replicaCertCopy: 6 | limits: 7 | cpu: 8 | memory: 9 | 10 | pgBackRest: 11 | repoHost: 12 | limits: 13 | cpu: 14 | memory: 15 | sidecars: 16 | limits: 17 | cpu: 18 | memory: 19 | 20 | proxy: 21 | pgBouncer: 22 | limits: 23 | cpu: 24 | memory: 25 | -------------------------------------------------------------------------------- /openshift/redash/crunchydb-postgres-values.yaml: -------------------------------------------------------------------------------- 1 | fullnameOverride: crunchy-postgres-redash 2 | 3 | postgresVersion: 16 4 | 5 | instances: 6 | replicas: 3 7 | dataVolumeClaimSpec: 8 | storage: 512Mi 9 | 10 | pgBackRest: 11 | repos: 12 | schedules: 13 | full: 0 12 * * * 14 | incremental: 0 0,4,8,16,20 * * * 15 | 16 | proxy: 17 | pgBouncer: 18 | replicas: 3 19 | -------------------------------------------------------------------------------- /openshift/redash/crunchydb-tools-values.yaml: -------------------------------------------------------------------------------- 1 | fullnameOverride: crunchy-postgres-tools-redash 2 | deploymentName: crunchy-postgres-redash 3 | -------------------------------------------------------------------------------- /openshift/redash/redash.route.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Template 3 | apiVersion: template.openshift.io/v1 4 | labels: 5 | app.kubernetes.io/component: server 6 | app.kubernetes.io/instance: ${NAME} 7 | app.kubernetes.io/managed-by: kubectl 8 | app.kubernetes.io/name: redash 9 | app.kubernetes.io/part-of: redash 10 | metadata: 11 | name: ${NAME} 12 | objects: 13 | - kind: Route 14 | apiVersion: route.openshift.io/v1 15 | metadata: 16 | name: ${NAME} 17 | spec: 18 | host: ${NAME}.apps.silver.devops.gov.bc.ca 19 | port: 20 | targetPort: http 21 | tls: 22 | insecureEdgeTerminationPolicy: Redirect 23 | termination: edge 24 | to: 25 | kind: Service 26 | name: ${NAME} 27 | weight: 100 28 | wildcardPolicy: None 29 | parameters: 30 | - name: NAME 31 | displayName: Name 32 | description: The name assigned to all of the objects defined in this template. 33 | required: true 34 | value: chefs-redash 35 | -------------------------------------------------------------------------------- /tests/functional/cypress/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | 'cypress' 4 | ], 5 | env: { 6 | mocha: true, 7 | 'cypress/globals': true 8 | }, 9 | rules: { 10 | strict: 'off' 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/functional/cypress/e2e/about.cy.js: -------------------------------------------------------------------------------- 1 | 2 | const depEnv = Cypress.env('depEnv'); 3 | const baseUrl = Cypress.env('baseUrl'); 4 | 5 | 6 | 7 | describe('Application About Page', () => { 8 | it('Visits the app about page', () => { 9 | 10 | if(depEnv=="") 11 | { 12 | cy.visit(`/app`); 13 | cy.contains('Create, publish forms, and receive submissions with the Common Hosted Forms Service.').should('be.visible'); 14 | } 15 | else 16 | { 17 | 18 | cy.visit(`/${depEnv}`); 19 | cy.contains('Create, publish forms, and receive submissions with the Common Hosted Forms Service.').should('be.visible'); 20 | cy.get('[data-test="base-auth-btn"] > .v-btn > .v-btn__content > span').click(); 21 | } 22 | 23 | }); 24 | }); -------------------------------------------------------------------------------- /tests/functional/cypress/fixtures/SamplePPTx.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcgov/common-hosted-form-service/ee209435541c048da1bc081f42fd5008ae415c06/tests/functional/cypress/fixtures/SamplePPTx.pptx -------------------------------------------------------------------------------- /tests/functional/cypress/fixtures/Testing_files.txt: -------------------------------------------------------------------------------- 1 | Testing cdogs.txt -------------------------------------------------------------------------------- /tests/functional/cypress/fixtures/add1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcgov/common-hosted-form-service/ee209435541c048da1bc081f42fd5008ae415c06/tests/functional/cypress/fixtures/add1.png -------------------------------------------------------------------------------- /tests/functional/cypress/fixtures/file_example_XLSX_50.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcgov/common-hosted-form-service/ee209435541c048da1bc081f42fd5008ae415c06/tests/functional/cypress/fixtures/file_example_XLSX_50.xlsx -------------------------------------------------------------------------------- /tests/functional/cypress/fixtures/formInitialBuilder/add1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcgov/common-hosted-form-service/ee209435541c048da1bc081f42fd5008ae415c06/tests/functional/cypress/fixtures/formInitialBuilder/add1.png -------------------------------------------------------------------------------- /tests/functional/cypress/fixtures/formInitialBuilder/ccHelpLinkInfoList.json: -------------------------------------------------------------------------------- 1 | {"Basic Layout":[{"id":"a96f3760-1444-4ac0-91c6-ec20793f5488","status":false,"componentName":"simplecols2","moreHelpInfoLink":"https://www.google.com/","imageUrl":"simplecols2.jpeg","version":7,"groupName":"Basic Layout","description":"Lorem Ipsum is"}]} -------------------------------------------------------------------------------- /tests/functional/cypress/fixtures/forms/empty-array.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /tests/functional/cypress/fixtures/forms/form-field.json: -------------------------------------------------------------------------------- 1 | ["simpletextfield"] 2 | -------------------------------------------------------------------------------- /tests/functional/cypress/fixtures/forms/form-submission-deleted.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "confirmationId": "ABF006D6", 4 | "createdAt": "2022-10-26T20:08:30.737Z", 5 | "formId": "1111111111-1111-1111-111111111111", 6 | "formSubmissionStatusCode": "SUBMITTED", 7 | "submissionId": "1111111111-1111-1111-111111111113", 8 | "deleted": true, 9 | "createdBy": "7ddfdd92d0c64cfc8d96a29a8a5bdcd7@idir@idir", 10 | "formVersionId": "1111111111-1111-1111-111111111112" 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /tests/functional/cypress/fixtures/forms/form-submission.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "confirmationId": "ABF006D6", 4 | "createdAt": "2022-10-26T20:08:30.737Z", 5 | "formId": "1111111111-1111-1111-111111111111", 6 | "formSubmissionStatusCode": "SUBMITTED", 7 | "submissionId": "1111111111-1111-1111-111111111113", 8 | "deleted": false, 9 | "createdBy": "7ddfdd92d0c64cfc8d96a29a8a5bdcd7@idir@idir", 10 | "formVersionId": "1111111111-1111-1111-111111111112" 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /tests/functional/cypress/fixtures/forms/form.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "1111111111-1111-1111-111111111111", 3 | "name": "test", 4 | "description": "", 5 | "active": true, 6 | "labels": null, 7 | "createdBy": "7ddfdd92d0c64cfc8d96a29a8a5bdcd7@idir@idir", 8 | "createdAt": "2022-10-26T20:06:56.435Z", 9 | "updatedBy": null, 10 | "updatedAt": "2022-10-26T20:06:56.420Z", 11 | "showSubmissionConfirmation": true, 12 | "submissionReceivedEmails": [], 13 | "enableStatusUpdates": false, 14 | "enableSubmitterDraft": false, 15 | "versions": [ 16 | { 17 | "id": "1111111111-1111-1111-111111111112", 18 | "formId": "1111111111-1111-1111-111111111111", 19 | "version": 1, 20 | "published": true, 21 | "createdBy": "7ddfdd92d0c64cfc8d96a29a8a5bdcd7@idir@idir", 22 | "createdAt": "2022-10-26T20:08:13.893Z", 23 | "updatedBy": null, 24 | "updatedAt": "2022-10-26T20:08:13.866Z" 25 | } 26 | ], 27 | "identityProviders": [], 28 | "snake": "test" 29 | } 30 | -------------------------------------------------------------------------------- /tests/functional/cypress/fixtures/kitchensink/formOptions.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "897e2ca1-d81c-4079-a135-63b930f98590", 3 | "name": "Kitchen Sink Simple", 4 | "description": "Test complicated form with the simple design interface", 5 | "idpHints": [ 6 | "public" 7 | ], 8 | "snake": "kitchen_sink_simple" 9 | } 10 | -------------------------------------------------------------------------------- /tests/functional/cypress/fixtures/kitchensink/submissionOptions.json: -------------------------------------------------------------------------------- 1 | { 2 | "submission": { 3 | "id": "3d2d6833-6e78-43e3-b332-6e66b6b8879c", 4 | "formVersionId": "897e2ca1-d81c-4079-a135-63b930f98590" 5 | }, 6 | "version": { 7 | "id": "897e2ca1-d81c-4079-a135-63b930f98590", 8 | "formId": "897e2ca1-d81c-4079-a135-63b930f98590" 9 | }, 10 | "form": { 11 | "id": "897e2ca1-d81c-4079-a135-63b930f98590", 12 | "name": "Kitchen Sink Simple", 13 | "description": "Test complicated form with the simple design interface", 14 | "idpHints": [ 15 | "public" 16 | ], 17 | "snake": "kitchen_sink_simple" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/functional/cypress/fixtures/test.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcgov/common-hosted-form-service/ee209435541c048da1bc081f42fd5008ae415c06/tests/functional/cypress/fixtures/test.docx -------------------------------------------------------------------------------- /tests/functional/cypress/fixtures/users/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "username": "admin", 3 | "password": "admin" 4 | } -------------------------------------------------------------------------------- /tests/functional/cypress/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chefs-cypress", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "Cypress functional test suite for CHEFS", 6 | "main": "index.js", 7 | "scripts": { 8 | "clean": "rm -rf **/screenshots **/videos", 9 | "purge": "rm -rf node_modules", 10 | "test": "cypress run", 11 | "test:allbrowsers": "npm run test:chrome && npm run test:edge && npm run test:firefox", 12 | "test:chrome": "npm run test -- --browser chrome", 13 | "test:dev": "cypress open", 14 | "test:edge": "npm run test -- --browser edge", 15 | "test:firefox": "npm run test -- --browser firefox" 16 | }, 17 | "keywords": [ 18 | "chefs", 19 | "cypress" 20 | ], 21 | "author": "", 22 | "license": "Apache-2.0", 23 | "dependencies": { 24 | "date-fns": "^2.26.0" 25 | }, 26 | "devDependencies": { 27 | "cypress": "14.2.0", 28 | "cypress-file-upload": "^5.0.8", 29 | "cypress-keycloak-commands": "^1.2.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/functional/cypress/support/component.js: -------------------------------------------------------------------------------- 1 | import { mount } from 'cypress/vue' 2 | 3 | Cypress.Commands.add('mount', mount) -------------------------------------------------------------------------------- /tests/functional/cypress/support/e2e.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/e2e.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') -------------------------------------------------------------------------------- /tests/functional/cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /tests/performance/.sample-env: -------------------------------------------------------------------------------- 1 | OIDC_TOKEN_URL=https://dev.loginproxy.gov.bc.ca/auth/realms/standard/protocol/openid-connect/token 2 | OIDC_CLIENT_ID=chefs-frontend-localhost-5300 3 | 4 | BASE_URL=http://localhost:8080/app 5 | FORM_NAME=01_kitchen_sink_advanced 6 | SCENARIOS=createAndPublishForm,fetchAndSubmitForm 7 | 8 | RPS=1 9 | 10 | TEST_RUN_USERS=1 11 | MIN_USERS=5 12 | MAX_USERS=50 13 | AVERAGE_USERS=35 14 | STRESS_LOAD_USERS=100 15 | 16 | STAGE=test_run 17 | 18 | INITIAL_TOKEN= 19 | INITIAL_REFRESH_TOKEN= 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/performance/common/auth.js: -------------------------------------------------------------------------------- 1 | export const INITIAL_TOKEN = __ENV.INITIAL_TOKEN; 2 | export const INITIAL_REFRESH_TOKEN = __ENV.INITIAL_REFRESH_TOKEN; 3 | 4 | export const generateRequestConfig = (token) => { 5 | return { 6 | headers: { 7 | "Content-Type": "application/json", 8 | Authorization: `Bearer ${token}`, 9 | }, 10 | }; 11 | }; 12 | --------------------------------------------------------------------------------