django-forms-workflows 0.76.4__tar.gz → 0.76.6__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.
Files changed (228) hide show
  1. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/PKG-INFO +1 -1
  2. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/admin.py +0 -4
  3. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/email_backends/gmail_api.py +1 -0
  4. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/forms.py +7 -19
  5. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/handlers/file_handler.py +4 -4
  6. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0064_add_reviewer_groups.py +0 -3
  7. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/sso_backends.py +8 -0
  8. django_forms_workflows-0.76.6/django_forms_workflows/static/django_forms_workflows/js/form-builder-api.js +411 -0
  9. django_forms_workflows-0.76.6/django_forms_workflows/static/django_forms_workflows/js/form-builder-canvas.js +1136 -0
  10. django_forms_workflows-0.76.6/django_forms_workflows/static/django_forms_workflows/js/form-builder-property-editor.js +730 -0
  11. django_forms_workflows-0.76.6/django_forms_workflows/static/django_forms_workflows/js/form-builder.js +129 -0
  12. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/static/django_forms_workflows/js/workflow-builder.js +16 -19
  13. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/tasks.py +2 -0
  14. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/admin/django_forms_workflows/workflow_builder.html +3 -2
  15. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/utils.py +4 -3
  16. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/views.py +2 -11
  17. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/workflow_builder_views.py +0 -1
  18. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/workflow_engine.py +0 -2
  19. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/pyproject.toml +1 -1
  20. django_forms_workflows-0.76.4/django_forms_workflows/static/django_forms_workflows/js/form-builder.js +0 -2338
  21. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/CHANGELOG.md +0 -0
  22. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/LICENSE +0 -0
  23. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/README.md +0 -0
  24. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/__init__.py +0 -0
  25. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/api_urls.py +0 -0
  26. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/api_views.py +0 -0
  27. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/apps.py +0 -0
  28. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/callback_registry.py +0 -0
  29. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/conditions.py +0 -0
  30. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/context_processors.py +0 -0
  31. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/data_sources/__init__.py +0 -0
  32. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/data_sources/base.py +0 -0
  33. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/data_sources/database_source.py +0 -0
  34. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/data_sources/ldap_source.py +0 -0
  35. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/data_sources/user_source.py +0 -0
  36. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/diff_views.py +0 -0
  37. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/email_backends/__init__.py +0 -0
  38. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/form_builder_urls.py +0 -0
  39. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/form_builder_views.py +0 -0
  40. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/handlers/__init__.py +0 -0
  41. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/handlers/api_handler.py +0 -0
  42. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/handlers/base.py +0 -0
  43. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/handlers/database_handler.py +0 -0
  44. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/handlers/email_handler.py +0 -0
  45. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/handlers/executor.py +0 -0
  46. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/handlers/ldap_handler.py +0 -0
  47. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/history.py +0 -0
  48. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/ldap_backend.py +0 -0
  49. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/management/commands/create_default_templates.py +0 -0
  50. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/management/commands/pull_forms.py +0 -0
  51. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/management/commands/push_forms.py +0 -0
  52. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/management/commands/seed_farm_demo.py +0 -0
  53. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/management/commands/seed_pcn_form.py +0 -0
  54. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/management/commands/seed_prefill_sources.py +0 -0
  55. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/management/commands/sync_ldap_profiles.py +0 -0
  56. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/management/commands/sync_ldap_users.py +0 -0
  57. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/management/commands/test_db_connection.py +0 -0
  58. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0001_initial.py +0 -0
  59. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0002_prefillsource_alter_formfield_prefill_source_and_more.py +0 -0
  60. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0003_postsubmissionaction.py +0 -0
  61. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0004_formtemplate.py +0 -0
  62. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0005_advanced_conditional_logic.py +0 -0
  63. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0006_workflowdefinition_visual_workflow_data_and_more.py +0 -0
  64. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0007_add_userprofile_ldap_enhancements.py +0 -0
  65. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0008_add_file_workflow_models.py +0 -0
  66. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0009_add_formfield_readonly.py +0 -0
  67. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0010_add_prefillsource_template_fields.py +0 -0
  68. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0011_postsubmissionaction_email_body_template_and_more.py +0 -0
  69. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0012_add_approval_step_fields.py +0 -0
  70. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0013_add_database_query_key.py +0 -0
  71. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0014_add_form_category.py +0 -0
  72. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0015_add_workflowstage_staged_approvals.py +0 -0
  73. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0016_add_pdf_generation.py +0 -0
  74. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0017_add_notification_batching.py +0 -0
  75. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0018_add_form_category_parent.py +0 -0
  76. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0019_add_ldap_group_profile.py +0 -0
  77. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0020_add_bulk_export_field.py +0 -0
  78. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0021_alter_pendingnotification_scheduled_for_and_more.py +0 -0
  79. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0022_add_bulk_pdf_export.py +0 -0
  80. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0023_add_performance_indexes.py +0 -0
  81. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0024_add_allow_resubmit.py +0 -0
  82. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0025_workflowstage_approve_label.py +0 -0
  83. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0026_alter_formfield_width.py +0 -0
  84. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0027_formsubmission_form_data_gin.py +0 -0
  85. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0028_workflowstagegroupconfig.py +0 -0
  86. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0029_formfield_workflow_stage_fk_drop_groupconfig.py +0 -0
  87. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0030_formfield_phone_type.py +0 -0
  88. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0031_sub_workflows.py +0 -0
  89. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0032_approved_pending_detached_sub_workflow.py +0 -0
  90. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0033_add_reject_parent_to_subworkflowdefinition.py +0 -0
  91. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0034_add_notificationlog.py +0 -0
  92. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0035_migrate_prefill_source_strings.py +0 -0
  93. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0036_add_workflowstage_auto_created.py +0 -0
  94. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0037_migrate_flat_workflows_to_staged.py +0 -0
  95. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0038_remove_legacy_prefill_source_and_approval_step.py +0 -0
  96. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0039_update_help_text.py +0 -0
  97. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0040_migrate_deprecated_features.py +0 -0
  98. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0041_remove_deprecated_fields.py +0 -0
  99. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0042_add_sub_workflow_section_label.py +0 -0
  100. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0043_fix_form_data_decimal_encoding.py +0 -0
  101. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0044_remove_subworkflowinstance_label.py +0 -0
  102. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0045_stageapprovalgroup_workflow_name_label.py +0 -0
  103. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0046_workflow_definition_one_to_many.py +0 -0
  104. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0047_add_hide_approval_history.py +0 -0
  105. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0048_workflowdefinition_trigger_conditions_and_more.py +0 -0
  106. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0049_formfield_multifile_type.py +0 -0
  107. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0050_workflowdefinition_collapse_parallel_stages.py +0 -0
  108. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0051_formdefinition_is_listed.py +0 -0
  109. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0052_dynamic_assignee_and_form_field_notifications.py +0 -0
  110. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0053_stageformfieldnotification_static_emails.py +0 -0
  111. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0054_rename_approved_pending_to_pending_approval.py +0 -0
  112. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0055_add_send_back.py +0 -0
  113. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0056_calculated_formula_spreadsheet.py +0 -0
  114. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0057_add_currency_field_type.py +0 -0
  115. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0058_rename_assignee_email_field_add_lookup_type.py +0 -0
  116. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0059_add_multiselect_list_field_type.py +0 -0
  117. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0060_alter_workflowstage_assignee_lookup_type_helptext.py +0 -0
  118. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0061_add_country_us_state_field_types.py +0 -0
  119. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0062_add_batch_import_to_formdefinition.py +0 -0
  120. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0063_formdefinition_api_enabled_apitoken.py +0 -0
  121. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0065_workflownotification.py +0 -0
  122. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0066_add_notify_submitter_to_workflownotification.py +0 -0
  123. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0067_migrate_legacy_notify_flags.py +0 -0
  124. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0068_drop_legacy_notify_fields.py +0 -0
  125. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0069_add_signature_field_type.py +0 -0
  126. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0070_workflowstage_allow_edit_form_data.py +0 -0
  127. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0071_alter_pendingnotification_notification_type.py +0 -0
  128. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0072_add_change_history.py +0 -0
  129. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0073_add_stage_notify_assignee_on_final_decision.py +0 -0
  130. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0074_add_notification_rule.py +0 -0
  131. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0075_migrate_to_notification_rules.py +0 -0
  132. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0076_update_notification_event_choices.py +0 -0
  133. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0077_drop_legacy_notification_models.py +0 -0
  134. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0078_allow_anonymous_submissions.py +0 -0
  135. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0079_webhookendpoint_webhookdeliverylog.py +0 -0
  136. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0080_add_rating_matrix_address_slider_fields_and_form_controls.py +0 -0
  137. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0081_add_success_page_fields.py +0 -0
  138. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0082_add_document_template_model.py +0 -0
  139. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0083_workflowdefinition_start_trigger.py +0 -0
  140. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0084_add_shared_option_list.py +0 -0
  141. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0085_add_payment_system.py +0 -0
  142. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0086_add_embed_enabled.py +0 -0
  143. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0087_notificationrule_use_triggering_stage.py +0 -0
  144. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0088_notificationrule_body_template_and_events.py +0 -0
  145. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0089_workflowstage_hide_comment_field.py +0 -0
  146. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0090_alter_stageapprovalgroup_options_and_more.py +0 -0
  147. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0091_add_uuid_fields.py +0 -0
  148. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0092_add_user_notification_preference.py +0 -0
  149. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0093_normalize_conditional_operators.py +0 -0
  150. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0094_formfield_show_help_text_in_detail.py +0 -0
  151. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0095_notificationrule_cc_fields.py +0 -0
  152. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0096_notificationlog_cc_bcc_emails.py +0 -0
  153. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/0097_notificationlog_delivery_reconciliation.py +0 -0
  154. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/migrations/__init__.py +0 -0
  155. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/models.py +0 -0
  156. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/payments/__init__.py +0 -0
  157. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/payments/base.py +0 -0
  158. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/payments/registry.py +0 -0
  159. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/payments/stripe_provider.py +0 -0
  160. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/payments/views.py +0 -0
  161. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/reporting_views.py +0 -0
  162. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/signals.py +0 -0
  163. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/sso_urls.py +0 -0
  164. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/sso_views.py +0 -0
  165. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/static/django_forms_workflows/api/swagger-ui-bundle.js +0 -0
  166. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/static/django_forms_workflows/api/swagger-ui.css +0 -0
  167. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/static/django_forms_workflows/css/forms.css +0 -0
  168. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/static/django_forms_workflows/js/admin_notification_rule_stage_filter.js +0 -0
  169. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/static/django_forms_workflows/js/dfw-embed.js +0 -0
  170. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/static/django_forms_workflows/js/form-builder-history.js +0 -0
  171. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/static/django_forms_workflows/js/form-builder-store.js +0 -0
  172. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/static/django_forms_workflows/js/form-enhancements.js +0 -0
  173. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/static/django_forms_workflows/js/payment-stripe.js +0 -0
  174. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/static/django_forms_workflows/js/signature-pad.js +0 -0
  175. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/sync_api.py +0 -0
  176. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/sync_views.py +0 -0
  177. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/admin/django_forms_workflows/_sync_diff.html +0 -0
  178. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/admin/django_forms_workflows/diff_forms.html +0 -0
  179. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/admin/django_forms_workflows/form_builder.html +0 -0
  180. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/admin/django_forms_workflows/formdef_change_form.html +0 -0
  181. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/admin/django_forms_workflows/formdefinition/change_list.html +0 -0
  182. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/admin/django_forms_workflows/sync_import.html +0 -0
  183. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/admin/django_forms_workflows/sync_pull.html +0 -0
  184. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/admin/django_forms_workflows/sync_push.html +0 -0
  185. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/_category_node.html +0 -0
  186. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/_field_value.html +0 -0
  187. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/_form_data_rows.html +0 -0
  188. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/analytics_dashboard.html +0 -0
  189. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/api/docs.html +0 -0
  190. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/approval_inbox.html +0 -0
  191. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/approve.html +0 -0
  192. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/base.html +0 -0
  193. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/batch_import_result.html +0 -0
  194. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/completed_approvals.html +0 -0
  195. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/discard_draft_confirm.html +0 -0
  196. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/embed_base.html +0 -0
  197. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/embed_success.html +0 -0
  198. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/form_categories.html +0 -0
  199. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/form_embed.html +0 -0
  200. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/form_list.html +0 -0
  201. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/form_submit.html +0 -0
  202. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/my_submissions.html +0 -0
  203. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/notification_preferences.html +0 -0
  204. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/payment_collect.html +0 -0
  205. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/payment_error.html +0 -0
  206. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/public_submission_confirmation.html +0 -0
  207. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/rate_limited.html +0 -0
  208. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/reassign_task.html +0 -0
  209. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/resubmit_confirm.html +0 -0
  210. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/sso/login.html +0 -0
  211. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/sub_workflow_detail.html +0 -0
  212. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/submission_bulk_pdf.html +0 -0
  213. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/submission_detail.html +0 -0
  214. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/submission_pdf.html +0 -0
  215. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/submission_success.html +0 -0
  216. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/django_forms_workflows/withdraw_confirm.html +0 -0
  217. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/emails/approval_notification.html +0 -0
  218. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/emails/approval_reminder.html +0 -0
  219. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/emails/approval_request.html +0 -0
  220. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/emails/email_styles.html +0 -0
  221. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/emails/escalation_notification.html +0 -0
  222. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/emails/notification_digest.html +0 -0
  223. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/emails/rejection_notification.html +0 -0
  224. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/emails/submission_notification.html +0 -0
  225. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templates/registration/login.html +0 -0
  226. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templatetags/__init__.py +0 -0
  227. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/templatetags/forms_workflows_tags.py +0 -0
  228. {django_forms_workflows-0.76.4 → django_forms_workflows-0.76.6}/django_forms_workflows/urls.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-forms-workflows
3
- Version: 0.76.4
3
+ Version: 0.76.6
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
@@ -278,8 +278,6 @@ def _render_conditional_rules(rules) -> str:
278
278
  [SHOW] when ``first_enrollment`` equals ``Yes``
279
279
  [REQUIRE] when ``first_enrollment`` equals ``Yes``
280
280
  """
281
- import json
282
-
283
281
  if not rules:
284
282
  return ""
285
283
  if isinstance(rules, str):
@@ -1279,8 +1277,6 @@ class FormDefinitionAdmin(nested_admin.NestedModelAdmin):
1279
1277
  remote_forms = payload.get("forms", [])
1280
1278
 
1281
1279
  # Build per-form diffs: remote vs local
1282
- import json
1283
-
1284
1280
  from .diff_views import _build_summary
1285
1281
 
1286
1282
  form_diffs = []
@@ -72,6 +72,7 @@ def _is_retryable_gmail_error(exc) -> bool:
72
72
  if err.get("reason") in _RETRYABLE_GMAIL_REASONS:
73
73
  return True
74
74
  except (ValueError, AttributeError, KeyError, TypeError):
75
+ # A malformed error payload is not enough evidence to retry safely.
75
76
  pass
76
77
  return False
77
78
 
@@ -341,6 +341,8 @@ class DynamicForm(forms.Form):
341
341
  try:
342
342
  self.stashed_files = json.loads(prev)
343
343
  except (json.JSONDecodeError, TypeError):
344
+ # Ignore malformed client metadata; uploaded files are rebuilt
345
+ # from validated server-side data below.
344
346
  pass
345
347
 
346
348
  # Build form fields from definition
@@ -1177,8 +1179,6 @@ class DynamicForm(forms.Form):
1177
1179
  Hidden fields are also dropped from ``cleaned_data`` so that their
1178
1180
  values are not persisted in the submission.
1179
1181
  """
1180
- import json
1181
-
1182
1182
  from .conditions import evaluate_conditions
1183
1183
 
1184
1184
  cleaned_data = super().clean()
@@ -1307,8 +1307,6 @@ class DynamicForm(forms.Form):
1307
1307
  Generate JavaScript configuration for form enhancements.
1308
1308
  Returns a dictionary that can be serialized to JSON.
1309
1309
  """
1310
- import json
1311
-
1312
1310
  from django.urls import reverse
1313
1311
 
1314
1312
  # Disable auto-save for anonymous users (no drafts without a user)
@@ -1554,8 +1552,6 @@ class ApprovalStepForm(forms.Form):
1554
1552
 
1555
1553
  def _add_field(self, field_def):
1556
1554
  """Add a single field to the form."""
1557
- is_editable = True
1558
-
1559
1555
  # Get current value from form data.
1560
1556
  # Sub-workflow fields are stored with an index suffix (e.g. payment_dept_code_1);
1561
1557
  # try the indexed key first, then fall back to the bare field name.
@@ -1572,16 +1568,16 @@ class ApprovalStepForm(forms.Form):
1572
1568
  current_value = field_def.default_value
1573
1569
 
1574
1570
  # Auto-fill approver name from current user
1575
- if is_editable and self._is_approver_name_field(field_def):
1571
+ if self._is_approver_name_field(field_def):
1576
1572
  current_value = self._get_approver_name()
1577
1573
  # Auto-fill date with current date
1578
- elif is_editable and self._is_date_field(field_def):
1574
+ elif self._is_date_field(field_def):
1579
1575
  current_value = date.today()
1580
1576
 
1581
1577
  # Common field arguments
1582
1578
  field_args = {
1583
1579
  "label": field_def.field_label,
1584
- "required": field_def.required if is_editable else False,
1580
+ "required": field_def.required,
1585
1581
  "help_text": field_def.help_text,
1586
1582
  "initial": current_value,
1587
1583
  }
@@ -1593,17 +1589,11 @@ class ApprovalStepForm(forms.Form):
1593
1589
  if field_def.css_class:
1594
1590
  widget_attrs["class"] = field_def.css_class
1595
1591
 
1596
- # Make non-editable fields read-only
1597
- if not is_editable:
1598
- widget_attrs["readonly"] = "readonly"
1599
- widget_attrs["disabled"] = "disabled"
1600
- field_args["required"] = False
1601
-
1602
1592
  if field_def.field_type in _PM_OPT_OUT_FIELD_TYPES:
1603
1593
  widget_attrs.update(_PM_OPT_OUT_ATTRS)
1604
1594
 
1605
1595
  # Create appropriate field type
1606
- self._create_field(field_def, field_args, widget_attrs, is_editable)
1596
+ self._create_field(field_def, field_args, widget_attrs)
1607
1597
 
1608
1598
  def _is_approver_name_field(self, field_def):
1609
1599
  """Check if this is an approver name field (to auto-fill)."""
@@ -1629,7 +1619,7 @@ class ApprovalStepForm(forms.Form):
1629
1619
  return self.user.username
1630
1620
  return ""
1631
1621
 
1632
- def _create_field(self, field_def, field_args, widget_attrs, is_editable):
1622
+ def _create_field(self, field_def, field_args, widget_attrs):
1633
1623
  """Create the appropriate Django form field."""
1634
1624
  if field_def.field_type == "text":
1635
1625
  if widget_attrs:
@@ -2002,8 +1992,6 @@ class ApprovalStepForm(forms.Form):
2002
1992
  validate fields on input/blur instead of waiting for submit, matching
2003
1993
  the behavior of the original submission form.
2004
1994
  """
2005
- import json
2006
-
2007
1995
  stage_id = self.approval_task.workflow_stage_id
2008
1996
  if stage_id is None:
2009
1997
  stage_fields = []
@@ -138,7 +138,7 @@ class FileOperationHandler:
138
138
  self.managed_file.file_path = new_path
139
139
  self.managed_file.save(update_fields=["stored_filename", "file_path"])
140
140
 
141
- logger.info(f"Renamed file from {old_path} to {new_path}")
141
+ logger.info("Renamed managed file id=%s", self.managed_file.id)
142
142
  return {"success": True, "message": f"Renamed to {new_filename}"}
143
143
  return {"success": False, "message": f"File not found: {old_path}"}
144
144
 
@@ -174,7 +174,7 @@ class FileOperationHandler:
174
174
  self.managed_file.stored_filename = os.path.basename(new_path)
175
175
  self.managed_file.save(update_fields=["file_path", "stored_filename"])
176
176
 
177
- logger.info(f"Moved file from {old_path} to {new_path}")
177
+ logger.info("Moved managed file id=%s", self.managed_file.id)
178
178
  return {"success": True, "message": f"Moved to {new_path}"}
179
179
  return {"success": False, "message": f"File not found: {old_path}"}
180
180
 
@@ -204,7 +204,7 @@ class FileOperationHandler:
204
204
  content = f.read()
205
205
  self.storage.save(new_path, content)
206
206
 
207
- logger.info(f"Copied file from {old_path} to {new_path}")
207
+ logger.info("Copied managed file id=%s", self.managed_file.id)
208
208
  return {"success": True, "message": f"Copied to {new_path}"}
209
209
  return {"success": False, "message": f"File not found: {old_path}"}
210
210
 
@@ -225,7 +225,7 @@ class FileOperationHandler:
225
225
  self.managed_file.status_changed_at = timezone.now()
226
226
  self.managed_file.save(update_fields=["status", "status_changed_at"])
227
227
 
228
- logger.info(f"Deleted file: {file_path}")
228
+ logger.info("Deleted managed file id=%s", self.managed_file.id)
229
229
  return {"success": True, "message": f"Deleted {file_path}"}
230
230
  return {"success": False, "message": f"File not found: {file_path}"}
231
231
 
@@ -1,9 +1,7 @@
1
- import django.db.models.deletion
2
1
  from django.db import migrations, models
3
2
 
4
3
 
5
4
  class Migration(migrations.Migration):
6
-
7
5
  dependencies = [
8
6
  ("django_forms_workflows", "0063_formdefinition_api_enabled_apitoken"),
9
7
  ]
@@ -24,4 +22,3 @@ class Migration(migrations.Migration):
24
22
  ),
25
23
  ),
26
24
  ]
27
-
@@ -253,6 +253,14 @@ def get_saml_config():
253
253
  This helper constructs the SAML settings from Django settings,
254
254
  providing sensible defaults for common use cases.
255
255
 
256
+ Compatibility note:
257
+ The default ``security`` configuration sets
258
+ ``wantAttributeStatement`` to ``False``. This is intentional to
259
+ support IdPs (including Google SAML in common configurations) that
260
+ may authenticate successfully without returning a SAML AttributeStatement.
261
+ Projects that require attributes can override this through
262
+ ``FORMS_WORKFLOWS_SAML["security"]``.
263
+
256
264
  Returns:
257
265
  dict: SAML configuration for OneLogin's python3-saml
258
266
  """
@@ -0,0 +1,411 @@
1
+ /**
2
+ * API client for the Form Builder: loading/saving the form definition,
3
+ * generating the live preview, and the template picker.
4
+ *
5
+ * Mixed onto FormBuilder.prototype in form-builder.js (Object.assign), not a
6
+ * standalone class of its own - these methods read/write a large number of
7
+ * page DOM fields directly, plus `this.fields`/`this.formSteps`/`this.config`,
8
+ * and call back into canvas-render methods (renderCanvas/renderStepTabs/
9
+ * toggleMultiStepMode) that still live on the single FormBuilder instance.
10
+ */
11
+ export const apiMethods = {
12
+ updatePreview() {
13
+ // Debounce preview updates to avoid too many API calls
14
+ if (this.previewTimeout) {
15
+ clearTimeout(this.previewTimeout);
16
+ }
17
+
18
+ this.previewTimeout = setTimeout(() => {
19
+ this.generatePreview();
20
+ }, 500); // Wait 500ms after last change
21
+ },
22
+
23
+ async generatePreview() {
24
+ const preview = document.getElementById('formPreview');
25
+
26
+ // Show loading state
27
+ preview.innerHTML = `
28
+ <div class="text-center text-muted py-4">
29
+ <div class="spinner-border spinner-border-sm" role="status">
30
+ <span class="visually-hidden">Loading...</span>
31
+ </div>
32
+ <p class="small mt-2">Generating preview...</p>
33
+ </div>
34
+ `;
35
+
36
+ try {
37
+ // Gather form data
38
+ const formData = {
39
+ name: document.getElementById('formName').value || 'Untitled Form',
40
+ slug: document.getElementById('formSlug').value || 'untitled-form',
41
+ description: document.getElementById('formDescription').value || '',
42
+ instructions: document.getElementById('formInstructions').value || '',
43
+ is_active: document.getElementById('formIsActive').checked,
44
+ requires_login: document.getElementById('formRequiresLogin').checked,
45
+ allow_save_draft: document.getElementById('formAllowDraft').checked,
46
+ allow_withdrawal: document.getElementById('formAllowWithdrawal').checked,
47
+ fields: this.fields
48
+ };
49
+
50
+ // Call preview API
51
+ const response = await fetch(this.config.apiUrls.preview, {
52
+ method: 'POST',
53
+ headers: {
54
+ 'Content-Type': 'application/json',
55
+ 'X-CSRFToken': this.config.csrfToken
56
+ },
57
+ body: JSON.stringify(formData)
58
+ });
59
+
60
+ if (!response.ok) {
61
+ const errorText = await response.text();
62
+ console.error('Preview API error:', response.status, errorText);
63
+ throw new Error(`Server error: ${response.status}`);
64
+ }
65
+
66
+ const data = await response.json();
67
+
68
+ if (data.success) {
69
+ preview.innerHTML = data.html;
70
+ } else {
71
+ console.error('Preview generation failed:', data.error);
72
+ throw new Error(data.error || 'Unknown error');
73
+ }
74
+
75
+ } catch (error) {
76
+ console.error('Preview error:', error);
77
+
78
+ // If there are no fields, show a helpful message
79
+ if (this.fields.length === 0) {
80
+ preview.innerHTML = `
81
+ <div class="alert alert-info small">
82
+ <i class="bi bi-info-circle"></i>
83
+ <strong>No fields yet</strong><br>
84
+ Drag fields from the palette to get started
85
+ </div>
86
+ `;
87
+ } else {
88
+ preview.innerHTML = `
89
+ <div class="alert alert-warning small">
90
+ <i class="bi bi-exclamation-triangle"></i>
91
+ <strong>Preview unavailable</strong><br>
92
+ ${this.fields.length} field(s) configured<br>
93
+ <small class="text-muted">${error.message}</small>
94
+ </div>
95
+ `;
96
+ }
97
+ }
98
+ },
99
+
100
+ async loadForm() {
101
+ try {
102
+ const response = await fetch(this.config.apiUrls.load, {
103
+ method: 'GET',
104
+ headers: {
105
+ 'X-CSRFToken': this.config.csrfToken
106
+ }
107
+ });
108
+
109
+ if (!response.ok) {
110
+ throw new Error('Failed to load form');
111
+ }
112
+
113
+ const data = await response.json();
114
+
115
+ // Populate form settings
116
+ document.getElementById('formName').value = data.name || '';
117
+ document.getElementById('formSlug').value = data.slug || '';
118
+ document.getElementById('formDescription').value = data.description || '';
119
+ document.getElementById('formInstructions').value = data.instructions || '';
120
+ document.getElementById('formIsActive').checked = data.is_active;
121
+ document.getElementById('formRequiresLogin').checked = data.requires_login;
122
+ document.getElementById('formAllowDraft').checked = data.allow_save_draft;
123
+ document.getElementById('formAllowWithdrawal').checked = data.allow_withdrawal;
124
+
125
+ // Load success page settings
126
+ document.getElementById('formSuccessMessage').value = data.success_message || '';
127
+ document.getElementById('formSuccessRedirectUrl').value = data.success_redirect_url || '';
128
+ document.getElementById('formSuccessRedirectRules').value = data.success_redirect_rules ? JSON.stringify(data.success_redirect_rules) : '';
129
+
130
+ // Load payment settings
131
+ document.getElementById('formPaymentEnabled').checked = data.payment_enabled || false;
132
+ if (data.payment_enabled) document.getElementById('paymentSettings').style.display = '';
133
+ // Populate provider dropdown
134
+ const providerSelect = document.getElementById('formPaymentProvider');
135
+ providerSelect.innerHTML = '<option value="">-- Select --</option>';
136
+ (this.config.paymentProviders || []).forEach(([val, label]) => {
137
+ const opt = document.createElement('option');
138
+ opt.value = val;
139
+ opt.textContent = label;
140
+ if (val === data.payment_provider) opt.selected = true;
141
+ providerSelect.appendChild(opt);
142
+ });
143
+ document.getElementById('formPaymentAmountType').value = data.payment_amount_type || 'fixed';
144
+ if (data.payment_fixed_amount) document.getElementById('formPaymentFixedAmount').value = data.payment_fixed_amount;
145
+ if (data.payment_amount_field) document.getElementById('formPaymentAmountField').value = data.payment_amount_field;
146
+ document.getElementById('formPaymentCurrency').value = data.payment_currency || 'usd';
147
+ if (data.payment_description_template) document.getElementById('formPaymentDescription').value = data.payment_description_template;
148
+
149
+ // Load submission controls
150
+ if (data.close_date) document.getElementById('formCloseDate').value = data.close_date.slice(0, 16);
151
+ if (data.max_submissions) document.getElementById('formMaxSubmissions').value = data.max_submissions;
152
+ document.getElementById('formOnePerUser').checked = data.one_per_user || false;
153
+ document.getElementById('formEnableCaptcha').checked = data.enable_captcha || false;
154
+ document.getElementById('formEmbedEnabled').checked = data.embed_enabled || false;
155
+
156
+ // Load client-side enhancement settings
157
+ document.getElementById('formEnableAutoSave').checked = data.enable_auto_save !== false;
158
+ document.getElementById('formAutoSaveInterval').value = data.auto_save_interval || 30;
159
+ document.getElementById('formEnableMultiStep').checked = data.enable_multi_step || false;
160
+ this.formSteps = data.form_steps || [];
161
+
162
+ // Load fields
163
+ this.fields = data.fields || [];
164
+ this.store.seedFieldIdCounterFromFields(this.fields);
165
+
166
+ // If multi-step is enabled, switch to multi-step mode
167
+ if (data.enable_multi_step) {
168
+ this.toggleMultiStepMode(true);
169
+ } else {
170
+ this.renderCanvas();
171
+ }
172
+
173
+ this.updatePreview();
174
+
175
+ document.getElementById('saveStatus').textContent = 'Loaded successfully';
176
+ } catch (error) {
177
+ console.error('Error loading form:', error);
178
+ alert('Failed to load form: ' + error.message);
179
+ }
180
+ },
181
+
182
+ async saveForm() {
183
+ // Validate form settings
184
+ const formName = document.getElementById('formName').value.trim();
185
+ const formSlug = document.getElementById('formSlug').value.trim();
186
+
187
+ if (!formName) {
188
+ alert('Please enter a form name');
189
+ return;
190
+ }
191
+
192
+ if (!formSlug) {
193
+ alert('Please enter a form slug');
194
+ return;
195
+ }
196
+
197
+ // If in multi-step mode, update field order from step canvases
198
+ const isMultiStep = document.getElementById('formEnableMultiStep').checked;
199
+ if (isMultiStep) {
200
+ this.updateFieldOrderFromSteps();
201
+ }
202
+
203
+ // Build form data
204
+ const formData = {
205
+ id: this.config.formId,
206
+ name: formName,
207
+ slug: formSlug,
208
+ description: document.getElementById('formDescription').value.trim(),
209
+ instructions: document.getElementById('formInstructions').value.trim(),
210
+ is_active: document.getElementById('formIsActive').checked,
211
+ requires_login: document.getElementById('formRequiresLogin').checked,
212
+ allow_save_draft: document.getElementById('formAllowDraft').checked,
213
+ allow_withdrawal: document.getElementById('formAllowWithdrawal').checked,
214
+ success_message: document.getElementById('formSuccessMessage').value.trim(),
215
+ success_redirect_url: document.getElementById('formSuccessRedirectUrl').value.trim(),
216
+ success_redirect_rules: (() => { try { const v = document.getElementById('formSuccessRedirectRules').value.trim(); return v ? JSON.parse(v) : null; } catch(e) { return null; } })(),
217
+ payment_enabled: document.getElementById('formPaymentEnabled').checked,
218
+ payment_provider: document.getElementById('formPaymentProvider').value,
219
+ payment_amount_type: document.getElementById('formPaymentAmountType').value,
220
+ payment_fixed_amount: document.getElementById('formPaymentFixedAmount').value || null,
221
+ payment_amount_field: document.getElementById('formPaymentAmountField').value,
222
+ payment_currency: document.getElementById('formPaymentCurrency').value,
223
+ payment_description_template: document.getElementById('formPaymentDescription').value,
224
+ close_date: document.getElementById('formCloseDate').value || null,
225
+ max_submissions: parseInt(document.getElementById('formMaxSubmissions').value) || null,
226
+ one_per_user: document.getElementById('formOnePerUser').checked,
227
+ enable_captcha: document.getElementById('formEnableCaptcha').checked,
228
+ embed_enabled: document.getElementById('formEmbedEnabled').checked,
229
+ enable_auto_save: document.getElementById('formEnableAutoSave').checked,
230
+ auto_save_interval: parseInt(document.getElementById('formAutoSaveInterval').value) || 30,
231
+ enable_multi_step: isMultiStep,
232
+ form_steps: this.formSteps || [],
233
+ fields: this.fields
234
+ };
235
+
236
+ // Show saving status
237
+ const saveBtn = document.getElementById('btnSave');
238
+ const originalText = saveBtn.innerHTML;
239
+ saveBtn.disabled = true;
240
+ saveBtn.innerHTML = '<i class="bi bi-hourglass-split"></i> Saving...';
241
+ document.getElementById('saveStatus').textContent = 'Saving...';
242
+
243
+ try {
244
+ const response = await fetch(this.config.apiUrls.save, {
245
+ method: 'POST',
246
+ headers: {
247
+ 'Content-Type': 'application/json',
248
+ 'X-CSRFToken': this.config.csrfToken
249
+ },
250
+ body: JSON.stringify(formData)
251
+ });
252
+
253
+ const result = await response.json();
254
+
255
+ if (!response.ok || !result.success) {
256
+ throw new Error(result.error || 'Failed to save form');
257
+ }
258
+
259
+ // Update field IDs with the mapping from backend
260
+ if (result.field_id_mapping) {
261
+ this.fields.forEach(field => {
262
+ const oldId = String(field.id);
263
+ if (result.field_id_mapping[oldId]) {
264
+ field.id = result.field_id_mapping[oldId];
265
+ }
266
+ });
267
+
268
+ // Re-render to update the UI with new IDs
269
+ const isMultiStep = document.getElementById('formEnableMultiStep')?.checked;
270
+ if (isMultiStep) {
271
+ this.renderStepTabs();
272
+ } else {
273
+ this.renderCanvas();
274
+ }
275
+ }
276
+
277
+ document.getElementById('saveStatus').textContent = 'Saved successfully';
278
+
279
+ // If this was a new form, redirect to edit mode
280
+ if (this.config.isNew && result.form_id) {
281
+ setTimeout(() => {
282
+ window.location.href = `/admin/django_forms_workflows/formdefinition/${result.form_id}/builder/`;
283
+ }, 1000);
284
+ } else {
285
+ // Show success message
286
+ setTimeout(() => {
287
+ document.getElementById('saveStatus').textContent = 'All changes saved';
288
+ }, 2000);
289
+ }
290
+ } catch (error) {
291
+ console.error('Error saving form:', error);
292
+ alert('Failed to save form: ' + error.message);
293
+ document.getElementById('saveStatus').textContent = 'Error saving';
294
+ } finally {
295
+ saveBtn.disabled = false;
296
+ saveBtn.innerHTML = originalText;
297
+ }
298
+ },
299
+
300
+ async showTemplateSelection() {
301
+ const modal = new bootstrap.Modal(document.getElementById('templateSelectionModal'));
302
+ const templateList = document.getElementById('templateList');
303
+
304
+ // Load templates
305
+ try {
306
+ const response = await fetch(this.config.apiUrls.templates);
307
+ const data = await response.json();
308
+
309
+ if (data.success && data.templates.length > 0) {
310
+ // Group templates by category
311
+ const grouped = {};
312
+ data.templates.forEach(template => {
313
+ if (!grouped[template.category_display]) {
314
+ grouped[template.category_display] = [];
315
+ }
316
+ grouped[template.category_display].push(template);
317
+ });
318
+
319
+ // Render templates
320
+ let html = '';
321
+ for (const [category, templates] of Object.entries(grouped)) {
322
+ html += `<div class="col-12"><h6 class="text-muted">${this.escapeHtml(String(category ?? ''))}</h6></div>`;
323
+ templates.forEach(template => {
324
+ html += `
325
+ <div class="col-md-4">
326
+ <div class="card template-card h-100" style="cursor: pointer;" data-template-id="${template.id}">
327
+ <div class="card-body">
328
+ <h6 class="card-title">${this.escapeHtml(template.name)}</h6>
329
+ <p class="card-text small text-muted">${this.escapeHtml(template.description)}</p>
330
+ <div class="d-flex justify-content-between align-items-center">
331
+ <small class="text-muted">
332
+ <i class="bi bi-people"></i> Used ${this.escapeHtml(String(template.usage_count ?? 0))} times
333
+ </small>
334
+ <button class="btn btn-sm btn-primary">Use Template</button>
335
+ </div>
336
+ </div>
337
+ </div>
338
+ </div>
339
+ `;
340
+ });
341
+ }
342
+ templateList.innerHTML = html;
343
+
344
+ // Add click handlers
345
+ document.querySelectorAll('.template-card').forEach(card => {
346
+ card.addEventListener('click', () => {
347
+ const templateId = card.dataset.templateId;
348
+ this.loadTemplate(templateId);
349
+ modal.hide();
350
+ });
351
+ });
352
+ } else {
353
+ templateList.innerHTML = `
354
+ <div class="col-12 text-center py-5">
355
+ <p class="text-muted">No templates available</p>
356
+ </div>
357
+ `;
358
+ }
359
+ } catch (error) {
360
+ console.error('Error loading templates:', error);
361
+ templateList.innerHTML = `
362
+ <div class="col-12 text-center py-5">
363
+ <p class="text-danger">Failed to load templates</p>
364
+ </div>
365
+ `;
366
+ }
367
+
368
+ // Handle "Start with Blank Form" button
369
+ document.getElementById('btnStartBlank').onclick = () => {
370
+ modal.hide();
371
+ };
372
+
373
+ modal.show();
374
+ },
375
+
376
+ async loadTemplate(templateId) {
377
+ try {
378
+ const url = this.config.apiUrls.loadTemplate.replace('{id}', templateId);
379
+ const response = await fetch(url);
380
+ const data = await response.json();
381
+
382
+ if (data.success && data.template_data) {
383
+ const templateData = data.template_data;
384
+
385
+ // Populate form settings
386
+ document.getElementById('formName').value = templateData.name || '';
387
+ document.getElementById('formSlug').value = templateData.slug || '';
388
+ document.getElementById('formDescription').value = templateData.description || '';
389
+ document.getElementById('formInstructions').value = templateData.instructions || '';
390
+ document.getElementById('formIsActive').checked = templateData.is_active !== false;
391
+ document.getElementById('formRequiresLogin').checked = templateData.requires_login !== false;
392
+ document.getElementById('formAllowDraft').checked = templateData.allow_save_draft !== false;
393
+ document.getElementById('formAllowWithdrawal').checked = templateData.allow_withdrawal !== false;
394
+
395
+ // Load fields
396
+ this.fields = templateData.fields || [];
397
+ this.store.seedFieldIdCounterFromFields(this.fields);
398
+ this.renderCanvas();
399
+ this.updatePreview();
400
+
401
+ // Show success message
402
+ alert('Template loaded successfully! You can now customize the form.');
403
+ } else {
404
+ throw new Error('Invalid template data');
405
+ }
406
+ } catch (error) {
407
+ console.error('Error loading template:', error);
408
+ alert('Failed to load template: ' + error.message);
409
+ }
410
+ },
411
+ };