django-forms-workflows 0.77.5__tar.gz → 0.78.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/PKG-INFO +12 -7
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/README.md +5 -3
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/admin.py +23 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/api_views.py +1 -1
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/apps.py +1 -1
- django_forms_workflows-0.78.0/django_forms_workflows/checks.py +24 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/diff_views.py +2 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/form_builder_views.py +14 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/forms.py +6 -25
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/models.py +3 -0
- django_forms_workflows-0.78.0/django_forms_workflows/pdf.py +53 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/reporting_views.py +44 -19
- django_forms_workflows-0.78.0/django_forms_workflows/security.py +108 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/sso_backends.py +30 -67
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/sso_views.py +8 -75
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/admin/django_forms_workflows/workflow_builder.html +2 -2
- django_forms_workflows-0.78.0/django_forms_workflows/uploads.py +104 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/utils.py +43 -31
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/views.py +56 -108
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/workflow_builder_views.py +17 -6
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/pyproject.toml +11 -5
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/CHANGELOG.md +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/LICENSE +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/__init__.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/api_urls.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/callback_registry.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/conditions.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/context_processors.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/data_sources/__init__.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/data_sources/base.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/data_sources/database_source.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/data_sources/ldap_source.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/data_sources/user_source.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/email_backends/__init__.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/email_backends/gmail_api.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/form_builder_urls.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/handlers/__init__.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/handlers/api_handler.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/handlers/base.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/handlers/database_handler.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/handlers/email_handler.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/handlers/executor.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/handlers/file_handler.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/handlers/ldap_handler.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/history.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/ldap_backend.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/management/commands/create_default_templates.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/management/commands/pull_forms.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/management/commands/push_forms.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/management/commands/seed_farm_demo.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/management/commands/seed_pcn_form.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/management/commands/seed_prefill_sources.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/management/commands/sync_ldap_profiles.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/management/commands/sync_ldap_users.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/management/commands/test_db_connection.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0001_initial.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0002_prefillsource_alter_formfield_prefill_source_and_more.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0003_postsubmissionaction.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0004_formtemplate.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0005_advanced_conditional_logic.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0006_workflowdefinition_visual_workflow_data_and_more.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0007_add_userprofile_ldap_enhancements.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0008_add_file_workflow_models.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0009_add_formfield_readonly.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0010_add_prefillsource_template_fields.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0011_postsubmissionaction_email_body_template_and_more.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0012_add_approval_step_fields.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0013_add_database_query_key.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0014_add_form_category.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0015_add_workflowstage_staged_approvals.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0016_add_pdf_generation.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0017_add_notification_batching.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0018_add_form_category_parent.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0019_add_ldap_group_profile.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0020_add_bulk_export_field.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0021_alter_pendingnotification_scheduled_for_and_more.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0022_add_bulk_pdf_export.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0023_add_performance_indexes.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0024_add_allow_resubmit.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0025_workflowstage_approve_label.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0026_alter_formfield_width.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0027_formsubmission_form_data_gin.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0028_workflowstagegroupconfig.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0029_formfield_workflow_stage_fk_drop_groupconfig.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0030_formfield_phone_type.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0031_sub_workflows.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0032_approved_pending_detached_sub_workflow.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0033_add_reject_parent_to_subworkflowdefinition.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0034_add_notificationlog.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0035_migrate_prefill_source_strings.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0036_add_workflowstage_auto_created.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0037_migrate_flat_workflows_to_staged.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0038_remove_legacy_prefill_source_and_approval_step.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0039_update_help_text.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0040_migrate_deprecated_features.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0041_remove_deprecated_fields.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0042_add_sub_workflow_section_label.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0043_fix_form_data_decimal_encoding.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0044_remove_subworkflowinstance_label.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0045_stageapprovalgroup_workflow_name_label.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0046_workflow_definition_one_to_many.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0047_add_hide_approval_history.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0048_workflowdefinition_trigger_conditions_and_more.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0049_formfield_multifile_type.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0050_workflowdefinition_collapse_parallel_stages.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0051_formdefinition_is_listed.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0052_dynamic_assignee_and_form_field_notifications.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0053_stageformfieldnotification_static_emails.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0054_rename_approved_pending_to_pending_approval.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0055_add_send_back.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0056_calculated_formula_spreadsheet.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0057_add_currency_field_type.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0058_rename_assignee_email_field_add_lookup_type.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0059_add_multiselect_list_field_type.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0060_alter_workflowstage_assignee_lookup_type_helptext.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0061_add_country_us_state_field_types.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0062_add_batch_import_to_formdefinition.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0063_formdefinition_api_enabled_apitoken.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0064_add_reviewer_groups.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0065_workflownotification.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0066_add_notify_submitter_to_workflownotification.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0067_migrate_legacy_notify_flags.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0068_drop_legacy_notify_fields.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0069_add_signature_field_type.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0070_workflowstage_allow_edit_form_data.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0071_alter_pendingnotification_notification_type.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0072_add_change_history.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0073_add_stage_notify_assignee_on_final_decision.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0074_add_notification_rule.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0075_migrate_to_notification_rules.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0076_update_notification_event_choices.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0077_drop_legacy_notification_models.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0078_allow_anonymous_submissions.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0079_webhookendpoint_webhookdeliverylog.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0080_add_rating_matrix_address_slider_fields_and_form_controls.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0081_add_success_page_fields.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0082_add_document_template_model.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0083_workflowdefinition_start_trigger.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0084_add_shared_option_list.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0085_add_payment_system.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0086_add_embed_enabled.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0087_notificationrule_use_triggering_stage.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0088_notificationrule_body_template_and_events.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0089_workflowstage_hide_comment_field.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0090_alter_stageapprovalgroup_options_and_more.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0091_add_uuid_fields.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0092_add_user_notification_preference.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0093_normalize_conditional_operators.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0094_formfield_show_help_text_in_detail.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0095_notificationrule_cc_fields.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0096_notificationlog_cc_bcc_emails.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/0097_notificationlog_delivery_reconciliation.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/migrations/__init__.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/payments/__init__.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/payments/base.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/payments/checkout.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/payments/registry.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/payments/stripe_provider.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/payments/views.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/signals.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/sso_urls.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/static/django_forms_workflows/api/swagger-ui-bundle.js +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/static/django_forms_workflows/api/swagger-ui.css +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/static/django_forms_workflows/css/forms.css +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/static/django_forms_workflows/js/admin_notification_rule_stage_filter.js +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/static/django_forms_workflows/js/dfw-embed.js +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/static/django_forms_workflows/js/form-builder-api.js +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/static/django_forms_workflows/js/form-builder-canvas.js +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/static/django_forms_workflows/js/form-builder-history.js +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/static/django_forms_workflows/js/form-builder-property-editor.js +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/static/django_forms_workflows/js/form-builder-store.js +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/static/django_forms_workflows/js/form-builder.js +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/static/django_forms_workflows/js/form-enhancements.js +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/static/django_forms_workflows/js/payment-stripe.js +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/static/django_forms_workflows/js/signature-pad.js +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/static/django_forms_workflows/js/workflow-builder-api.js +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/static/django_forms_workflows/js/workflow-builder-store.js +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/static/django_forms_workflows/js/workflow-builder.js +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/sync_api.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/sync_views.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/tasks.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/admin/django_forms_workflows/_sync_diff.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/admin/django_forms_workflows/diff_forms.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/admin/django_forms_workflows/form_builder.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/admin/django_forms_workflows/formdef_change_form.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/admin/django_forms_workflows/formdefinition/change_list.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/admin/django_forms_workflows/sync_import.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/admin/django_forms_workflows/sync_pull.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/admin/django_forms_workflows/sync_push.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/_category_node.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/_field_value.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/_form_data_rows.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/analytics_dashboard.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/api/docs.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/approval_inbox.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/approve.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/base.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/batch_import_result.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/completed_approvals.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/discard_draft_confirm.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/embed_base.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/embed_success.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/form_categories.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/form_embed.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/form_list.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/form_submit.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/my_submissions.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/notification_preferences.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/payment_collect.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/payment_error.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/public_submission_confirmation.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/rate_limited.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/reassign_task.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/resubmit_confirm.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/sso/login.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/sub_workflow_detail.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/submission_bulk_pdf.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/submission_detail.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/submission_pdf.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/submission_success.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/django_forms_workflows/withdraw_confirm.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/emails/approval_notification.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/emails/approval_reminder.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/emails/approval_request.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/emails/email_styles.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/emails/escalation_notification.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/emails/notification_digest.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/emails/rejection_notification.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/emails/submission_notification.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templates/registration/login.html +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templatetags/__init__.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/templatetags/forms_workflows_tags.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/urls.py +0 -0
- {django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/workflow_engine.py +0 -0
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-forms-workflows
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.78.0
|
|
4
4
|
Summary: Enterprise-grade, database-driven form builder with approval workflows and external data integration
|
|
5
5
|
License: LGPL-3.0-only
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Keywords: django,forms,workflows,approval,form-builder,dynamic-forms,ldap,enterprise,audit,database-driven,sso,saml,oauth2
|
|
8
8
|
Author: Matt Davis
|
|
9
9
|
Author-email: matt@opensensor.io
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.11,<4.0
|
|
11
11
|
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.14
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.15
|
|
19
18
|
Provides-Extra: all
|
|
19
|
+
Provides-Extra: cache
|
|
20
20
|
Provides-Extra: dev
|
|
21
21
|
Provides-Extra: docs
|
|
22
22
|
Provides-Extra: excel
|
|
@@ -31,7 +31,7 @@ Provides-Extra: postgresql
|
|
|
31
31
|
Provides-Extra: qr
|
|
32
32
|
Provides-Extra: saml
|
|
33
33
|
Provides-Extra: sso
|
|
34
|
-
Requires-Dist: Django (>=5.
|
|
34
|
+
Requires-Dist: Django (>=5.2.18,<7.0,!=6.0.0,!=6.0.1,!=6.0.2,!=6.0.3,!=6.0.4,!=6.0.5,!=6.0.6,!=6.0.7,!=6.0.8,!=6.1.0,!=6.1.1)
|
|
35
35
|
Requires-Dist: celery (>=5.3)
|
|
36
36
|
Requires-Dist: crispy-bootstrap5 (>=2.0)
|
|
37
37
|
Requires-Dist: django-auth-ldap (>=4.0) ; extra == "ldap" or extra == "all"
|
|
@@ -44,7 +44,9 @@ Requires-Dist: google-auth (>=2.20) ; extra == "gmail" or extra == "all"
|
|
|
44
44
|
Requires-Dist: markdown (>=3.4) ; extra == "markdown" or extra == "all"
|
|
45
45
|
Requires-Dist: mssql-django (>=1.3) ; extra == "mssql" or extra == "all"
|
|
46
46
|
Requires-Dist: mysqlclient (>=2.2) ; extra == "mysql" or extra == "all"
|
|
47
|
+
Requires-Dist: nh3 (>=0.3.2)
|
|
47
48
|
Requires-Dist: openpyxl (>=3.1) ; extra == "excel" or extra == "all"
|
|
49
|
+
Requires-Dist: pillow (>=13.0.0) ; extra == "pdf" or extra == "all"
|
|
48
50
|
Requires-Dist: psycopg2-binary (>=2.9) ; extra == "postgresql" or extra == "all"
|
|
49
51
|
Requires-Dist: pyodbc (>=5.0) ; extra == "mssql" or extra == "all"
|
|
50
52
|
Requires-Dist: pytest (>=7.4) ; extra == "dev"
|
|
@@ -53,6 +55,7 @@ Requires-Dist: pytest-django (>=4.5) ; extra == "dev"
|
|
|
53
55
|
Requires-Dist: python-decouple (>=3.8)
|
|
54
56
|
Requires-Dist: python-ldap (>=3.4) ; extra == "ldap" or extra == "all"
|
|
55
57
|
Requires-Dist: python3-saml (>=1.16) ; extra == "sso" or extra == "saml" or extra == "all"
|
|
58
|
+
Requires-Dist: redis (>=5.0) ; extra == "cache" or extra == "all"
|
|
56
59
|
Requires-Dist: requests (>=2.31)
|
|
57
60
|
Requires-Dist: ruff (>=0.4) ; extra == "dev"
|
|
58
61
|
Requires-Dist: segno (>=1.6) ; extra == "qr" or extra == "all"
|
|
@@ -72,7 +75,7 @@ Description-Content-Type: text/markdown
|
|
|
72
75
|
**Enterprise-grade, database-driven form builder with multi-stage approval workflows, external data integration, and cross-instance sync**
|
|
73
76
|
|
|
74
77
|
[](https://www.gnu.org/licenses/lgpl-3.0)
|
|
75
|
-
[](https://www.python.org/downloads/)
|
|
76
79
|
[](https://www.djangoproject.com/)
|
|
77
80
|
[](https://github.com/opensensor/django-forms-workflows)
|
|
78
81
|
|
|
@@ -258,6 +261,8 @@ Move form definitions between environments from the Django Admin:
|
|
|
258
261
|
|
|
259
262
|
## Quick Start
|
|
260
263
|
|
|
264
|
+
See [security upgrade notes](docs/SECURITY_UPGRADE.md) before upgrading an existing deployment.
|
|
265
|
+
|
|
261
266
|
### Installation
|
|
262
267
|
|
|
263
268
|
```bash
|
|
@@ -413,8 +418,8 @@ graph TB
|
|
|
413
418
|
|
|
414
419
|
## Requirements
|
|
415
420
|
|
|
416
|
-
- Python 3.
|
|
417
|
-
- Django 5.2+
|
|
421
|
+
- Python 3.11+
|
|
422
|
+
- Django 5.2.18+, 6.0.9+, or 6.1.2+ (security patch minimums)
|
|
418
423
|
- PostgreSQL, MySQL, or SQLite (PostgreSQL recommended for production)
|
|
419
424
|
- Optional: Celery 5.0+ with Redis/Valkey for background task processing
|
|
420
425
|
- Optional: `openpyxl` for Excel spreadsheet field support (`pip install django-forms-workflows[excel]`)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
**Enterprise-grade, database-driven form builder with multi-stage approval workflows, external data integration, and cross-instance sync**
|
|
4
4
|
|
|
5
5
|
[](https://www.gnu.org/licenses/lgpl-3.0)
|
|
6
|
-
[](https://www.python.org/downloads/)
|
|
7
7
|
[](https://www.djangoproject.com/)
|
|
8
8
|
[](https://github.com/opensensor/django-forms-workflows)
|
|
9
9
|
|
|
@@ -189,6 +189,8 @@ Move form definitions between environments from the Django Admin:
|
|
|
189
189
|
|
|
190
190
|
## Quick Start
|
|
191
191
|
|
|
192
|
+
See [security upgrade notes](docs/SECURITY_UPGRADE.md) before upgrading an existing deployment.
|
|
193
|
+
|
|
192
194
|
### Installation
|
|
193
195
|
|
|
194
196
|
```bash
|
|
@@ -344,8 +346,8 @@ graph TB
|
|
|
344
346
|
|
|
345
347
|
## Requirements
|
|
346
348
|
|
|
347
|
-
- Python 3.
|
|
348
|
-
- Django 5.2+
|
|
349
|
+
- Python 3.11+
|
|
350
|
+
- Django 5.2.18+, 6.0.9+, or 6.1.2+ (security patch minimums)
|
|
349
351
|
- PostgreSQL, MySQL, or SQLite (PostgreSQL recommended for production)
|
|
350
352
|
- Optional: Celery 5.0+ with Redis/Valkey for background task processing
|
|
351
353
|
- Optional: `openpyxl` for Excel spreadsheet field support (`pip install django-forms-workflows[excel]`)
|
{django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/admin.py
RENAMED
|
@@ -1154,6 +1154,15 @@ class FormDefinitionAdmin(nested_admin.NestedModelAdmin):
|
|
|
1154
1154
|
|
|
1155
1155
|
def sync_import_admin_view(self, request):
|
|
1156
1156
|
"""Admin page for importing form definitions from a JSON file or raw JSON."""
|
|
1157
|
+
from django.core.exceptions import PermissionDenied
|
|
1158
|
+
|
|
1159
|
+
if not self.has_view_permission(request):
|
|
1160
|
+
raise PermissionDenied
|
|
1161
|
+
if not (
|
|
1162
|
+
self.has_add_permission(request) and self.has_change_permission(request)
|
|
1163
|
+
):
|
|
1164
|
+
raise PermissionDenied
|
|
1165
|
+
|
|
1157
1166
|
from .sync_api import import_payload
|
|
1158
1167
|
|
|
1159
1168
|
context = dict(self.admin_site.each_context(request))
|
|
@@ -1219,6 +1228,15 @@ class FormDefinitionAdmin(nested_admin.NestedModelAdmin):
|
|
|
1219
1228
|
Step 1 (POST) – fetch available forms from the remote, show checkbox list
|
|
1220
1229
|
Step 2 (POST) – import selected forms, show results
|
|
1221
1230
|
"""
|
|
1231
|
+
from django.core.exceptions import PermissionDenied
|
|
1232
|
+
|
|
1233
|
+
if not self.has_view_permission(request):
|
|
1234
|
+
raise PermissionDenied
|
|
1235
|
+
if not (
|
|
1236
|
+
self.has_add_permission(request) and self.has_change_permission(request)
|
|
1237
|
+
):
|
|
1238
|
+
raise PermissionDenied
|
|
1239
|
+
|
|
1222
1240
|
from .sync_api import (
|
|
1223
1241
|
build_export_payload,
|
|
1224
1242
|
fetch_remote_payload,
|
|
@@ -1372,6 +1390,11 @@ class FormDefinitionAdmin(nested_admin.NestedModelAdmin):
|
|
|
1372
1390
|
Step 1 (POST) – show all local forms with checkboxes + diff status
|
|
1373
1391
|
Step 2 (POST) – execute push for selected forms, show results
|
|
1374
1392
|
"""
|
|
1393
|
+
from django.core.exceptions import PermissionDenied
|
|
1394
|
+
|
|
1395
|
+
if not self.has_view_permission(request):
|
|
1396
|
+
raise PermissionDenied
|
|
1397
|
+
|
|
1375
1398
|
from .sync_api import (
|
|
1376
1399
|
build_export_payload,
|
|
1377
1400
|
fetch_remote_payload,
|
{django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/api_views.py
RENAMED
|
@@ -106,7 +106,7 @@ def require_api_token(view_func):
|
|
|
106
106
|
raw = auth.removeprefix("Bearer ").strip()
|
|
107
107
|
try:
|
|
108
108
|
token_obj = APIToken.objects.select_related("user").get(
|
|
109
|
-
token=raw, is_active=True
|
|
109
|
+
token=raw, is_active=True, user__is_active=True
|
|
110
110
|
)
|
|
111
111
|
except (APIToken.DoesNotExist, ValueError, Exception): # noqa: BLE001
|
|
112
112
|
return JsonResponse({"error": "Invalid or inactive token."}, status=401)
|
{django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/apps.py
RENAMED
|
@@ -17,7 +17,7 @@ class DjangoFormsWorkflowsConfig(AppConfig):
|
|
|
17
17
|
Import signal handlers and perform app initialization.
|
|
18
18
|
"""
|
|
19
19
|
# Import signals to register handlers
|
|
20
|
-
from . import signals # noqa: F401
|
|
20
|
+
from . import checks, signals # noqa: F401
|
|
21
21
|
|
|
22
22
|
# Load custom callback handlers from FORMS_WORKFLOWS_CALLBACKS setting
|
|
23
23
|
from .callback_registry import load_from_settings
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Deployment checks for public submission throttling."""
|
|
2
|
+
|
|
3
|
+
from django.conf import settings
|
|
4
|
+
from django.core.checks import Tags, Warning, register
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@register(Tags.security, deploy=True)
|
|
8
|
+
def check_rate_limit_cache(app_configs, **kwargs):
|
|
9
|
+
alias = getattr(settings, "FORMS_WORKFLOWS_RATE_LIMIT_CACHE", "default")
|
|
10
|
+
backend = settings.CACHES.get(alias, {}).get("BACKEND", "")
|
|
11
|
+
if backend not in {
|
|
12
|
+
"django.core.cache.backends.redis.RedisCache",
|
|
13
|
+
"django.core.cache.backends.memcached.PyMemcacheCache",
|
|
14
|
+
"django.core.cache.backends.memcached.PyLibMCCache",
|
|
15
|
+
"django_redis.cache.RedisCache",
|
|
16
|
+
}:
|
|
17
|
+
return [
|
|
18
|
+
Warning(
|
|
19
|
+
"Public form rate limiting requires a shared cache with atomic add/incr.",
|
|
20
|
+
hint="Configure Redis or Memcached and FORMS_WORKFLOWS_RATE_LIMIT_CACHE; local-memory caches only limit one process.",
|
|
21
|
+
id="django_forms_workflows.W001",
|
|
22
|
+
)
|
|
23
|
+
]
|
|
24
|
+
return []
|
{django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/diff_views.py
RENAMED
|
@@ -8,6 +8,7 @@ from django.http import HttpResponseBadRequest
|
|
|
8
8
|
from django.shortcuts import render
|
|
9
9
|
|
|
10
10
|
from .models import FormDefinition
|
|
11
|
+
from .security import builder_permission_required
|
|
11
12
|
from .sync_api import build_export_payload
|
|
12
13
|
|
|
13
14
|
|
|
@@ -429,6 +430,7 @@ def _build_summary(forms_data):
|
|
|
429
430
|
return summaries
|
|
430
431
|
|
|
431
432
|
|
|
433
|
+
@builder_permission_required("view_formdefinition")
|
|
432
434
|
def diff_forms_view(request):
|
|
433
435
|
"""Render a side-by-side JSON diff of selected FormDefinitions."""
|
|
434
436
|
pks = request.GET.get("pks", "")
|
|
@@ -26,11 +26,13 @@ from .models import (
|
|
|
26
26
|
WorkflowDefinition,
|
|
27
27
|
WorkflowStage,
|
|
28
28
|
)
|
|
29
|
+
from .security import builder_permission_required
|
|
29
30
|
|
|
30
31
|
logger = logging.getLogger(__name__)
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
@require_GET
|
|
35
|
+
@builder_permission_required("view_formtemplate")
|
|
34
36
|
def form_builder_templates(request):
|
|
35
37
|
"""
|
|
36
38
|
API endpoint to list available form templates.
|
|
@@ -63,6 +65,7 @@ def form_builder_templates(request):
|
|
|
63
65
|
|
|
64
66
|
|
|
65
67
|
@require_GET
|
|
68
|
+
@builder_permission_required("view_formtemplate")
|
|
66
69
|
def form_builder_load_template(request, template_id):
|
|
67
70
|
"""
|
|
68
71
|
API endpoint to load a form template.
|
|
@@ -83,6 +86,7 @@ def form_builder_load_template(request, template_id):
|
|
|
83
86
|
|
|
84
87
|
|
|
85
88
|
@require_POST
|
|
89
|
+
@builder_permission_required("view_formdefinition", "add_formdefinition")
|
|
86
90
|
def form_builder_clone(request, form_id):
|
|
87
91
|
"""
|
|
88
92
|
API endpoint to clone an existing form.
|
|
@@ -225,6 +229,7 @@ def form_builder_clone(request, form_id):
|
|
|
225
229
|
|
|
226
230
|
|
|
227
231
|
@require_GET
|
|
232
|
+
@builder_permission_required("view_formdefinition")
|
|
228
233
|
def form_builder_view(request, form_id=None):
|
|
229
234
|
"""
|
|
230
235
|
Main form builder page.
|
|
@@ -269,6 +274,7 @@ def form_builder_view(request, form_id=None):
|
|
|
269
274
|
|
|
270
275
|
|
|
271
276
|
@require_GET
|
|
277
|
+
@builder_permission_required("view_formdefinition")
|
|
272
278
|
def form_builder_load(request, form_id):
|
|
273
279
|
"""
|
|
274
280
|
API endpoint to load form data as JSON.
|
|
@@ -353,6 +359,7 @@ def form_builder_load(request, form_id):
|
|
|
353
359
|
|
|
354
360
|
|
|
355
361
|
@require_POST
|
|
362
|
+
@builder_permission_required("save_formdefinition")
|
|
356
363
|
def form_builder_save(request):
|
|
357
364
|
"""
|
|
358
365
|
API endpoint to save form data.
|
|
@@ -604,6 +611,7 @@ def save_form_definition_from_builder_data(data, user, form_definition=None):
|
|
|
604
611
|
|
|
605
612
|
|
|
606
613
|
@require_POST
|
|
614
|
+
@builder_permission_required("view_formdefinition")
|
|
607
615
|
def form_builder_preview(request):
|
|
608
616
|
"""
|
|
609
617
|
API endpoint to generate a preview of the form.
|
|
@@ -764,6 +772,7 @@ def form_builder_preview(request):
|
|
|
764
772
|
|
|
765
773
|
|
|
766
774
|
@require_GET
|
|
775
|
+
@builder_permission_required("view_formdefinition", "view_documenttemplate")
|
|
767
776
|
def document_template_list(request, form_id):
|
|
768
777
|
"""List document templates for a form."""
|
|
769
778
|
templates = DocumentTemplate.objects.filter(form_definition_id=form_id).order_by(
|
|
@@ -789,6 +798,7 @@ def document_template_list(request, form_id):
|
|
|
789
798
|
|
|
790
799
|
|
|
791
800
|
@require_POST
|
|
801
|
+
@builder_permission_required("change_formdefinition", "save_documenttemplate")
|
|
792
802
|
def document_template_save(request, form_id):
|
|
793
803
|
"""Create or update a document template."""
|
|
794
804
|
try:
|
|
@@ -847,6 +857,7 @@ def document_template_save(request, form_id):
|
|
|
847
857
|
|
|
848
858
|
|
|
849
859
|
@require_POST
|
|
860
|
+
@builder_permission_required("change_formdefinition", "delete_documenttemplate")
|
|
850
861
|
def document_template_delete(request, form_id, template_id):
|
|
851
862
|
"""Delete a document template."""
|
|
852
863
|
tpl = get_object_or_404(
|
|
@@ -862,6 +873,7 @@ def document_template_delete(request, form_id, template_id):
|
|
|
862
873
|
|
|
863
874
|
|
|
864
875
|
@require_GET
|
|
876
|
+
@builder_permission_required("view_sharedoptionlist")
|
|
865
877
|
def shared_option_list_api(request):
|
|
866
878
|
"""List all shared option lists (for form builder dropdowns)."""
|
|
867
879
|
lists = SharedOptionList.objects.filter(is_active=True).order_by("name")
|
|
@@ -882,6 +894,7 @@ def shared_option_list_api(request):
|
|
|
882
894
|
|
|
883
895
|
|
|
884
896
|
@require_POST
|
|
897
|
+
@builder_permission_required("save_sharedoptionlist")
|
|
885
898
|
def shared_option_list_save(request):
|
|
886
899
|
"""Create or update a shared option list."""
|
|
887
900
|
try:
|
|
@@ -924,6 +937,7 @@ def shared_option_list_save(request):
|
|
|
924
937
|
|
|
925
938
|
|
|
926
939
|
@require_POST
|
|
940
|
+
@builder_permission_required("delete_sharedoptionlist")
|
|
927
941
|
def shared_option_list_delete(request, list_id):
|
|
928
942
|
"""Delete a shared option list."""
|
|
929
943
|
ol = get_object_or_404(SharedOptionList, id=list_id)
|
{django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/forms.py
RENAMED
|
@@ -333,18 +333,6 @@ class DynamicForm(forms.Form):
|
|
|
333
333
|
# files). Also used to carry forward draft / resubmit file data.
|
|
334
334
|
self.stashed_files = stashed_files or {}
|
|
335
335
|
|
|
336
|
-
# Recover stashed-file metadata carried forward via hidden input from
|
|
337
|
-
# a prior validation-failure render (browsers cannot re-send files).
|
|
338
|
-
if not self.stashed_files and self.data:
|
|
339
|
-
prev = self.data.get("_stashed_files")
|
|
340
|
-
if prev:
|
|
341
|
-
try:
|
|
342
|
-
self.stashed_files = json.loads(prev)
|
|
343
|
-
except (json.JSONDecodeError, TypeError):
|
|
344
|
-
# Ignore malformed client metadata; uploaded files are rebuilt
|
|
345
|
-
# from validated server-side data below.
|
|
346
|
-
pass
|
|
347
|
-
|
|
348
336
|
# Build form fields from definition
|
|
349
337
|
# Exclude fields scoped to a workflow stage - those are for approvers only
|
|
350
338
|
for field in (
|
|
@@ -354,15 +342,6 @@ class DynamicForm(forms.Form):
|
|
|
354
342
|
):
|
|
355
343
|
self.add_field(field, initial_data)
|
|
356
344
|
|
|
357
|
-
# Embed stashed-file metadata as a hidden field so it survives
|
|
358
|
-
# validation-failure round-trips (browsers cannot re-send files).
|
|
359
|
-
if self.stashed_files:
|
|
360
|
-
self.fields["_stashed_files"] = forms.CharField(
|
|
361
|
-
widget=forms.HiddenInput(),
|
|
362
|
-
required=False,
|
|
363
|
-
initial=json.dumps(self.stashed_files),
|
|
364
|
-
)
|
|
365
|
-
|
|
366
345
|
# Setup form layout with Crispy Forms
|
|
367
346
|
self.helper = FormHelper()
|
|
368
347
|
self.helper.form_method = "post"
|
|
@@ -763,7 +742,7 @@ class DynamicForm(forms.Form):
|
|
|
763
742
|
# failure, saved with a draft, or carried from a resubmission).
|
|
764
743
|
existing_file = self._get_existing_file_info(field_def.field_name)
|
|
765
744
|
if existing_file:
|
|
766
|
-
fname = existing_file.get("filename", "file")
|
|
745
|
+
fname = html_escape(existing_file.get("filename", "file"))
|
|
767
746
|
extra_help = f"Previously uploaded: <strong>{fname}</strong>. Leave blank to keep it."
|
|
768
747
|
base_help = field_args.get("help_text", "") or ""
|
|
769
748
|
field_args["help_text"] = (
|
|
@@ -789,10 +768,11 @@ class DynamicForm(forms.Form):
|
|
|
789
768
|
if isinstance(existing_file, list)
|
|
790
769
|
else existing_file.get("filename", "file")
|
|
791
770
|
)
|
|
792
|
-
extra = f"Previously uploaded: <strong>{names}</strong>. Leave blank to keep."
|
|
771
|
+
extra = f"Previously uploaded: <strong>{html_escape(names)}</strong>. Leave blank to keep."
|
|
793
772
|
help_text = f"{help_text} {extra}" if help_text else extra
|
|
794
773
|
required = False
|
|
795
774
|
self.fields[field_def.field_name] = MultipleFileField(
|
|
775
|
+
validators=_build_file_validators(field_def),
|
|
796
776
|
required=required,
|
|
797
777
|
label=field_def.field_label,
|
|
798
778
|
help_text=help_text,
|
|
@@ -931,12 +911,13 @@ class DynamicForm(forms.Form):
|
|
|
931
911
|
)
|
|
932
912
|
|
|
933
913
|
elif field_def.field_type == "display_text":
|
|
934
|
-
display_html = _render_display_text(
|
|
914
|
+
display_html = _render_display_text(field_def.default_value)
|
|
935
915
|
self.fields[field_def.field_name] = forms.CharField(
|
|
936
916
|
required=False,
|
|
937
917
|
label=field_def.field_label,
|
|
938
918
|
help_text=field_def.help_text,
|
|
939
|
-
initial=
|
|
919
|
+
initial=field_def.default_value,
|
|
920
|
+
disabled=True,
|
|
940
921
|
widget=DisplayTextWidget(
|
|
941
922
|
display_html=display_html,
|
|
942
923
|
attrs={"data-display-text": "true"},
|
{django_forms_workflows-0.77.5 → django_forms_workflows-0.78.0}/django_forms_workflows/models.py
RENAMED
|
@@ -881,6 +881,8 @@ class DocumentTemplate(models.Model):
|
|
|
881
881
|
"""Render this template with submission data, returning HTML string."""
|
|
882
882
|
import re
|
|
883
883
|
|
|
884
|
+
from django.utils.html import escape
|
|
885
|
+
|
|
884
886
|
form_data = submission.form_data or {}
|
|
885
887
|
|
|
886
888
|
# Build merge context with system variables + form data
|
|
@@ -906,6 +908,7 @@ class DocumentTemplate(models.Model):
|
|
|
906
908
|
else:
|
|
907
909
|
context[key] = str(val) if val is not None else ""
|
|
908
910
|
|
|
911
|
+
context = {key: str(escape(value)) for key, value in context.items()}
|
|
909
912
|
html = self.html_content
|
|
910
913
|
|
|
911
914
|
# Process conditional blocks: {% if field_name %}...{% endif %}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""PDF resources are offline by default, including CSS and SVG subresources."""
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import binascii
|
|
5
|
+
|
|
6
|
+
from django.conf import settings
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def safe_pdf_url_fetcher(url, *args, **kwargs):
|
|
10
|
+
"""Allow bounded embedded raster images; never open files or network URLs.
|
|
11
|
+
|
|
12
|
+
Logos/signatures should be embedded as data:image/png;base64,... (or JPEG).
|
|
13
|
+
A deployment needing additional resources can supply a reviewed fetcher via
|
|
14
|
+
FORMS_WORKFLOWS_PDF_URL_FETCHER. It must enforce its own allowlist and limits.
|
|
15
|
+
"""
|
|
16
|
+
limit = getattr(settings, "FORMS_WORKFLOWS_PDF_MAX_RESOURCE_BYTES", 5 * 1024 * 1024)
|
|
17
|
+
if len(url) > limit * 4 // 3 + 128:
|
|
18
|
+
raise ValueError("PDF resource exceeds size limit")
|
|
19
|
+
header, sep, encoded = url.partition(",")
|
|
20
|
+
if not sep or header not in ("data:image/png;base64", "data:image/jpeg;base64"):
|
|
21
|
+
raise ValueError("PDF resource URL is not permitted")
|
|
22
|
+
try:
|
|
23
|
+
data = base64.b64decode(encoded, validate=True)
|
|
24
|
+
except (ValueError, binascii.Error) as exc:
|
|
25
|
+
raise ValueError("Invalid PDF image") from exc
|
|
26
|
+
mime = header[5:].split(";")[0]
|
|
27
|
+
signature = b"\x89PNG\r\n\x1a\n" if mime == "image/png" else b"\xff\xd8\xff"
|
|
28
|
+
if len(data) > limit or not data.startswith(signature):
|
|
29
|
+
raise ValueError("Invalid PDF image")
|
|
30
|
+
return {"string": data, "mime_type": mime}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def pdf_url_fetcher():
|
|
34
|
+
from django.utils.module_loading import import_string
|
|
35
|
+
|
|
36
|
+
custom = getattr(settings, "FORMS_WORKFLOWS_PDF_URL_FETCHER", None)
|
|
37
|
+
if custom:
|
|
38
|
+
factory = import_string(custom)
|
|
39
|
+
return factory()
|
|
40
|
+
# WeasyPrint 70 replaced dictionary-returning callables with URLFetcher.
|
|
41
|
+
try:
|
|
42
|
+
from weasyprint.urls import URLFetcher, URLFetcherResponse
|
|
43
|
+
except ImportError:
|
|
44
|
+
return safe_pdf_url_fetcher
|
|
45
|
+
|
|
46
|
+
class OfflineFetcher(URLFetcher):
|
|
47
|
+
def fetch(self, url, headers=None):
|
|
48
|
+
resource = safe_pdf_url_fetcher(url)
|
|
49
|
+
return URLFetcherResponse(
|
|
50
|
+
url, resource["string"], {"Content-Type": resource["mime_type"]}
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
return OfflineFetcher(allowed_protocols={"data"}, allow_redirects=False)
|
|
@@ -18,17 +18,31 @@ from django.shortcuts import render
|
|
|
18
18
|
from django.utils import timezone
|
|
19
19
|
from django.utils.safestring import mark_safe
|
|
20
20
|
|
|
21
|
-
from .models import ApprovalTask, FormDefinition
|
|
21
|
+
from .models import ApprovalTask, FormDefinition
|
|
22
|
+
from .security import visible_submissions
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _range_days(request):
|
|
26
|
+
try:
|
|
27
|
+
return max(1, min(int(request.GET.get("days", 90)), 3660))
|
|
28
|
+
except (TypeError, ValueError):
|
|
29
|
+
return 90
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _csv_cell(value):
|
|
33
|
+
if isinstance(value, str) and value.lstrip().startswith(("=", "+", "-", "@")):
|
|
34
|
+
return "'" + value
|
|
35
|
+
return value
|
|
22
36
|
|
|
23
37
|
|
|
24
38
|
@login_required
|
|
25
39
|
def analytics_export_csv(request):
|
|
26
40
|
"""Export analytics summary data as CSV."""
|
|
27
|
-
range_days =
|
|
41
|
+
range_days = _range_days(request)
|
|
28
42
|
cutoff = timezone.now() - timedelta(days=range_days)
|
|
29
43
|
form_slug = request.GET.get("form", "")
|
|
30
44
|
|
|
31
|
-
submissions_qs =
|
|
45
|
+
submissions_qs = visible_submissions(request.user).exclude(status="draft")
|
|
32
46
|
if form_slug:
|
|
33
47
|
submissions_qs = submissions_qs.filter(form_definition__slug=form_slug)
|
|
34
48
|
|
|
@@ -46,15 +60,18 @@ def analytics_export_csv(request):
|
|
|
46
60
|
).order_by("-created_at"):
|
|
47
61
|
writer.writerow(
|
|
48
62
|
[
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
sub.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
63
|
+
_csv_cell(value)
|
|
64
|
+
for value in [
|
|
65
|
+
sub.created_at.strftime("%Y-%m-%d %H:%M"),
|
|
66
|
+
sub.form_definition.name if sub.form_definition else "",
|
|
67
|
+
sub.status,
|
|
68
|
+
(
|
|
69
|
+
sub.submitter.get_full_name() or sub.submitter.username
|
|
70
|
+
if sub.submitter
|
|
71
|
+
else "Anonymous"
|
|
72
|
+
),
|
|
73
|
+
sub.id,
|
|
74
|
+
]
|
|
58
75
|
]
|
|
59
76
|
)
|
|
60
77
|
|
|
@@ -66,14 +83,14 @@ def analytics_dashboard(request):
|
|
|
66
83
|
"""Render the analytics dashboard with submission & approval metrics."""
|
|
67
84
|
|
|
68
85
|
# ── Time range filter ───────────────────────────────────────────────
|
|
69
|
-
range_days =
|
|
86
|
+
range_days = _range_days(request)
|
|
70
87
|
cutoff = timezone.now() - timedelta(days=range_days)
|
|
71
88
|
prev_cutoff = cutoff - timedelta(days=range_days) # Previous period
|
|
72
89
|
form_slug = request.GET.get("form", "")
|
|
73
90
|
|
|
74
91
|
# Base querysets
|
|
75
|
-
submissions_qs =
|
|
76
|
-
tasks_qs = ApprovalTask.objects.
|
|
92
|
+
submissions_qs = visible_submissions(request.user).exclude(status="draft")
|
|
93
|
+
tasks_qs = ApprovalTask.objects.filter(submission__in=submissions_qs)
|
|
77
94
|
|
|
78
95
|
if form_slug:
|
|
79
96
|
submissions_qs = submissions_qs.filter(form_definition__slug=form_slug)
|
|
@@ -195,7 +212,7 @@ def analytics_dashboard(request):
|
|
|
195
212
|
# ── 7. Bottleneck stages ────────────────────────────────────────────
|
|
196
213
|
# Stages with the most pending tasks right now
|
|
197
214
|
bottleneck_stages = list(
|
|
198
|
-
|
|
215
|
+
tasks_qs.filter(status="pending", workflow_stage__isnull=False)
|
|
199
216
|
.values(
|
|
200
217
|
"workflow_stage__name", "workflow_stage__workflow__form_definition__name"
|
|
201
218
|
)
|
|
@@ -232,13 +249,16 @@ def analytics_dashboard(request):
|
|
|
232
249
|
|
|
233
250
|
# ── Available forms for filter dropdown ─────────────────────────────
|
|
234
251
|
available_forms = list(
|
|
235
|
-
FormDefinition.objects.filter(
|
|
252
|
+
FormDefinition.objects.filter(
|
|
253
|
+
is_active=True, submissions__in=visible_submissions(request.user)
|
|
254
|
+
)
|
|
255
|
+
.distinct()
|
|
236
256
|
.values_list("slug", "name")
|
|
237
257
|
.order_by("name")
|
|
238
258
|
)
|
|
239
259
|
|
|
240
260
|
# ── 10. Overdue tasks ───────────────────────────────────────────────
|
|
241
|
-
overdue_count =
|
|
261
|
+
overdue_count = tasks_qs.filter(
|
|
242
262
|
status="pending",
|
|
243
263
|
due_date__isnull=False,
|
|
244
264
|
due_date__lt=timezone.now(),
|
|
@@ -256,7 +276,12 @@ def analytics_dashboard(request):
|
|
|
256
276
|
else:
|
|
257
277
|
clean[k] = v
|
|
258
278
|
serializable.append(clean)
|
|
259
|
-
return mark_safe(
|
|
279
|
+
return mark_safe(
|
|
280
|
+
json.dumps(serializable)
|
|
281
|
+
.replace("<", "\\u003C")
|
|
282
|
+
.replace(">", "\\u003E")
|
|
283
|
+
.replace("&", "\\u0026")
|
|
284
|
+
)
|
|
260
285
|
|
|
261
286
|
context = {
|
|
262
287
|
"range_days": range_days,
|